Completed
Push — develop ( 685e29...09d456 )
by Adrien
14:14
created

Excel5::readRecordData()   C

Complexity

Conditions 9
Paths 5

Size

Total Lines 46
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

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

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

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

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

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

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

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

Loading history...
Bug introduced by
Are you sure the assignment to $res is correct as $ole->read($pFilename) (which targets PhpSpreadsheet\Shared\OLERead::read()) seems to always return null.

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

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

}

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

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

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

Loading history...
432
433
            return true;
434
        } catch (\PhpSpreadsheet\Exception $e) {
435
            return false;
436
        }
437
    }
438
439
    /**
440
     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object
441
     *
442
     * @param     string         $pFilename
443
     * @throws     Exception
444
     */
445
    public function listWorksheetNames($pFilename)
446
    {
447
        // Check if file exists
448
        if (!file_exists($pFilename)) {
449
            throw new Exception('Could not open ' . $pFilename . ' for reading! File does not exist.');
450
        }
451
452
        $worksheetNames = [];
453
454
        // Read the OLE file
455
        $this->loadOLE($pFilename);
456
457
        // total byte size of Excel data (workbook global substream + sheet substreams)
458
        $this->dataSize = strlen($this->data);
459
460
        $this->pos = 0;
461
        $this->sheets = [];
462
463
        // Parse Workbook Global Substream
464 View Code Duplication
        while ($this->pos < $this->dataSize) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

Loading history...
522
            $code = self::getInt2d($this->data, $this->pos);
523
524
            switch ($code) {
525
                case self::XLS_TYPE_BOF:
526
                    $this->readBof();
527
                    break;
528
                case self::XLS_TYPE_SHEET:
529
                    $this->readSheet();
530
                    break;
531
                case self::XLS_TYPE_EOF:
532
                    $this->readDefault();
533
                    break 2;
534
                default:
535
                    $this->readDefault();
536
                    break;
537
            }
538
        }
539
540
        // Parse the individual sheets
541
        foreach ($this->sheets as $sheet) {
542
            if ($sheet['sheetType'] != 0x00) {
543
                // 0x00: Worksheet
544
                // 0x02: Chart
545
                // 0x06: Visual Basic module
546
                continue;
547
            }
548
549
            $tmpInfo = [];
550
            $tmpInfo['worksheetName'] = $sheet['name'];
551
            $tmpInfo['lastColumnLetter'] = 'A';
552
            $tmpInfo['lastColumnIndex'] = 0;
553
            $tmpInfo['totalRows'] = 0;
554
            $tmpInfo['totalColumns'] = 0;
555
556
            $this->pos = $sheet['offset'];
557
558
            while ($this->pos <= $this->dataSize - 4) {
559
                $code = self::getInt2d($this->data, $this->pos);
560
561
                switch ($code) {
562
                    case self::XLS_TYPE_RK:
563
                    case self::XLS_TYPE_LABELSST:
564
                    case self::XLS_TYPE_NUMBER:
565
                    case self::XLS_TYPE_FORMULA:
566
                    case self::XLS_TYPE_BOOLERR:
567
                    case self::XLS_TYPE_LABEL:
568
                        $length = self::getInt2d($this->data, $this->pos + 2);
569
                        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
570
571
                        // move stream pointer to next record
572
                        $this->pos += 4 + $length;
573
574
                        $rowIndex = self::getInt2d($recordData, 0) + 1;
575
                        $columnIndex = self::getInt2d($recordData, 2);
576
577
                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
578
                        $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
579
                        break;
580
                    case self::XLS_TYPE_BOF:
581
                        $this->readBof();
582
                        break;
583
                    case self::XLS_TYPE_EOF:
584
                        $this->readDefault();
585
                        break 2;
586
                    default:
587
                        $this->readDefault();
588
                        break;
589
                }
590
            }
591
592
            $tmpInfo['lastColumnLetter'] = \PhpSpreadsheet\Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
593
            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
594
595
            $worksheetInfo[] = $tmpInfo;
596
        }
597
598
        return $worksheetInfo;
599
    }
600
601
    /**
602
     * Loads PhpSpreadsheet from file
603
     *
604
     * @param     string         $pFilename
605
     * @throws    Exception
606
     * @return    \PhpSpreadsheet\Spreadsheet
607
     */
608
    public function load($pFilename)
609
    {
610
        // Read the OLE file
611
        $this->loadOLE($pFilename);
612
613
        // Initialisations
614
        $this->spreadsheet = new \PhpSpreadsheet\Spreadsheet();
615
        $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
616
        if (!$this->readDataOnly) {
617
            $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
618
            $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
619
        }
620
621
        // Read the summary information stream (containing meta data)
622
        $this->readSummaryInformation();
623
624
        // Read the Additional document summary information stream (containing application-specific meta data)
625
        $this->readDocumentSummaryInformation();
626
627
        // total byte size of Excel data (workbook global substream + sheet substreams)
628
        $this->dataSize = strlen($this->data);
629
630
        // initialize
631
        $this->pos = 0;
632
        $this->codepage = 'CP1252';
633
        $this->formats = [];
634
        $this->objFonts = [];
635
        $this->palette = [];
636
        $this->sheets = [];
637
        $this->externalBooks = [];
638
        $this->ref = [];
639
        $this->definedname = [];
640
        $this->sst = [];
641
        $this->drawingGroupData = '';
642
        $this->xfIndex = '';
0 ignored issues
show
Documentation Bug introduced by
The property $xfIndex was declared of type integer, but '' is of type string. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
758
                    $borderDiagonalColor = Excel5\Color::map($diagonal->colorIndex, $this->palette, $this->version);
759
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
760
                }
761
            }
762
        }
763
764
        // treat MSODRAWINGGROUP records, workbook-level Escher
765
        if (!$this->readDataOnly && $this->drawingGroupData) {
766
            $escherWorkbook = new \PhpSpreadsheet\Shared\Escher();
767
            $reader = new Excel5\Escher($escherWorkbook);
768
            $escherWorkbook = $reader->load($this->drawingGroupData);
769
        }
770
771
        // Parse the individual sheets
772
        foreach ($this->sheets as $sheet) {
773
            if ($sheet['sheetType'] != 0x00) {
774
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
775
                continue;
776
            }
777
778
            // check if sheet should be skipped
779
            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
780
                continue;
781
            }
782
783
            // add sheet to PhpSpreadsheet object
784
            $this->phpSheet = $this->spreadsheet->createSheet();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->spreadsheet->createSheet() of type object<PhpSpreadsheet\Worksheet> is incompatible with the declared type object<PhpSpreadsheet\Reader\Worksheet> of property $phpSheet.

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

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

Loading history...
785
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
786
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
787
            //        name in line with the formula, not the reverse
788
            $this->phpSheet->setTitle($sheet['name'], false);
789
            $this->phpSheet->setSheetState($sheet['sheetState']);
790
791
            $this->pos = $sheet['offset'];
792
793
            // Initialize isFitToPages. May change after reading SHEETPR record.
794
            $this->isFitToPages = false;
795
796
            // Initialize drawingData
797
            $this->drawingData = '';
798
799
            // Initialize objs
800
            $this->objs = [];
801
802
            // Initialize shared formula parts
803
            $this->sharedFormulaParts = [];
804
805
            // Initialize shared formulas
806
            $this->sharedFormulas = [];
807
808
            // Initialize text objs
809
            $this->textObjects = [];
810
811
            // Initialize cell annotations
812
            $this->cellNotes = [];
813
            $this->textObjRef = -1;
0 ignored issues
show
Bug introduced by
The property textObjRef does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
814
815
            while ($this->pos <= $this->dataSize - 4) {
816
                $code = self::getInt2d($this->data, $this->pos);
817
818
                switch ($code) {
819
                    case self::XLS_TYPE_BOF:
820
                        $this->readBof();
821
                        break;
822
                    case self::XLS_TYPE_PRINTGRIDLINES:
823
                        $this->readPrintGridlines();
824
                        break;
825
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
826
                        $this->readDefaultRowHeight();
827
                        break;
828
                    case self::XLS_TYPE_SHEETPR:
829
                        $this->readSheetPr();
830
                        break;
831
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
832
                        $this->readHorizontalPageBreaks();
833
                        break;
834
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
835
                        $this->readVerticalPageBreaks();
836
                        break;
837
                    case self::XLS_TYPE_HEADER:
838
                        $this->readHeader();
839
                        break;
840
                    case self::XLS_TYPE_FOOTER:
841
                        $this->readFooter();
842
                        break;
843
                    case self::XLS_TYPE_HCENTER:
844
                        $this->readHcenter();
845
                        break;
846
                    case self::XLS_TYPE_VCENTER:
847
                        $this->readVcenter();
848
                        break;
849
                    case self::XLS_TYPE_LEFTMARGIN:
850
                        $this->readLeftMargin();
851
                        break;
852
                    case self::XLS_TYPE_RIGHTMARGIN:
853
                        $this->readRightMargin();
854
                        break;
855
                    case self::XLS_TYPE_TOPMARGIN:
856
                        $this->readTopMargin();
857
                        break;
858
                    case self::XLS_TYPE_BOTTOMMARGIN:
859
                        $this->readBottomMargin();
860
                        break;
861
                    case self::XLS_TYPE_PAGESETUP:
862
                        $this->readPageSetup();
863
                        break;
864
                    case self::XLS_TYPE_PROTECT:
865
                        $this->readProtect();
866
                        break;
867
                    case self::XLS_TYPE_SCENPROTECT:
868
                        $this->readScenProtect();
869
                        break;
870
                    case self::XLS_TYPE_OBJECTPROTECT:
871
                        $this->readObjectProtect();
872
                        break;
873
                    case self::XLS_TYPE_PASSWORD:
874
                        $this->readPassword();
875
                        break;
876
                    case self::XLS_TYPE_DEFCOLWIDTH:
877
                        $this->readDefColWidth();
878
                        break;
879
                    case self::XLS_TYPE_COLINFO:
880
                        $this->readColInfo();
881
                        break;
882
                    case self::XLS_TYPE_DIMENSION:
883
                        $this->readDefault();
884
                        break;
885
                    case self::XLS_TYPE_ROW:
886
                        $this->readRow();
887
                        break;
888
                    case self::XLS_TYPE_DBCELL:
889
                        $this->readDefault();
890
                        break;
891
                    case self::XLS_TYPE_RK:
892
                        $this->readRk();
893
                        break;
894
                    case self::XLS_TYPE_LABELSST:
895
                        $this->readLabelSst();
896
                        break;
897
                    case self::XLS_TYPE_MULRK:
898
                        $this->readMulRk();
899
                        break;
900
                    case self::XLS_TYPE_NUMBER:
901
                        $this->readNumber();
902
                        break;
903
                    case self::XLS_TYPE_FORMULA:
904
                        $this->readFormula();
905
                        break;
906
                    case self::XLS_TYPE_SHAREDFMLA:
907
                        $this->readSharedFmla();
908
                        break;
909
                    case self::XLS_TYPE_BOOLERR:
910
                        $this->readBoolErr();
911
                        break;
912
                    case self::XLS_TYPE_MULBLANK:
913
                        $this->readMulBlank();
914
                        break;
915
                    case self::XLS_TYPE_LABEL:
916
                        $this->readLabel();
917
                        break;
918
                    case self::XLS_TYPE_BLANK:
919
                        $this->readBlank();
920
                        break;
921
                    case self::XLS_TYPE_MSODRAWING:
922
                        $this->readMsoDrawing();
923
                        break;
924
                    case self::XLS_TYPE_OBJ:
925
                        $this->readObj();
926
                        break;
927
                    case self::XLS_TYPE_WINDOW2:
928
                        $this->readWindow2();
929
                        break;
930
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
931
                        $this->readPageLayoutView();
932
                        break;
933
                    case self::XLS_TYPE_SCL:
934
                        $this->readScl();
935
                        break;
936
                    case self::XLS_TYPE_PANE:
937
                        $this->readPane();
938
                        break;
939
                    case self::XLS_TYPE_SELECTION:
940
                        $this->readSelection();
941
                        break;
942
                    case self::XLS_TYPE_MERGEDCELLS:
943
                        $this->readMergedCells();
944
                        break;
945
                    case self::XLS_TYPE_HYPERLINK:
946
                        $this->readHyperLink();
947
                        break;
948
                    case self::XLS_TYPE_DATAVALIDATIONS:
949
                        $this->readDataValidations();
950
                        break;
951
                    case self::XLS_TYPE_DATAVALIDATION:
952
                        $this->readDataValidation();
953
                        break;
954
                    case self::XLS_TYPE_SHEETLAYOUT:
955
                        $this->readSheetLayout();
956
                        break;
957
                    case self::XLS_TYPE_SHEETPROTECTION:
958
                        $this->readSheetProtection();
959
                        break;
960
                    case self::XLS_TYPE_RANGEPROTECTION:
961
                        $this->readRangeProtection();
962
                        break;
963
                    case self::XLS_TYPE_NOTE:
964
                        $this->readNote();
965
                        break;
966
                    //case self::XLS_TYPE_IMDATA:                $this->readImData();                    break;
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
967
                    case self::XLS_TYPE_TXO:
968
                        $this->readTextObject();
969
                        break;
970
                    case self::XLS_TYPE_CONTINUE:
971
                        $this->readContinue();
972
                        break;
973
                    case self::XLS_TYPE_EOF:
974
                        $this->readDefault();
975
                        break 2;
976
                    default:
977
                        $this->readDefault();
978
                        break;
979
                }
980
            }
981
982
            // treat MSODRAWING records, sheet-level Escher
983
            if (!$this->readDataOnly && $this->drawingData) {
984
                $escherWorksheet = new \PhpSpreadsheet\Shared\Escher();
985
                $reader = new Excel5\Escher($escherWorksheet);
986
                $escherWorksheet = $reader->load($this->drawingData);
987
988
                // get all spContainers in one long array, so they can be mapped to OBJ records
989
                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
990
            }
991
992
            // treat OBJ records
993
            foreach ($this->objs as $n => $obj) {
994
                //                echo '<hr /><b>Object</b> reference is ', $n,'<br />';
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...
995
//                var_dump($obj);
996
//                echo '<br />';
997
998
                // the first shape container never has a corresponding OBJ record, hence $n + 1
999
                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
1000
                    $spContainer = $allSpContainers[$n + 1];
1001
1002
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
1003
                    if ($spContainer->getNestingLevel() > 1) {
1004
                        continue;
1005
                    }
1006
1007
                    // calculate the width and height of the shape
1008
                    list($startColumn, $startRow) = \PhpSpreadsheet\Cell::coordinateFromString($spContainer->getStartCoordinates());
1009
                    list($endColumn, $endRow) = \PhpSpreadsheet\Cell::coordinateFromString($spContainer->getEndCoordinates());
1010
1011
                    $startOffsetX = $spContainer->getStartOffsetX();
1012
                    $startOffsetY = $spContainer->getStartOffsetY();
1013
                    $endOffsetX = $spContainer->getEndOffsetX();
1014
                    $endOffsetY = $spContainer->getEndOffsetY();
1015
1016
                    $width = \PhpSpreadsheet\Shared\Excel5::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
1017
                    $height = \PhpSpreadsheet\Shared\Excel5::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
1018
1019
                    // calculate offsetX and offsetY of the shape
1020
                    $offsetX = $startOffsetX * \PhpSpreadsheet\Shared\Excel5::sizeCol($this->phpSheet, $startColumn) / 1024;
1021
                    $offsetY = $startOffsetY * \PhpSpreadsheet\Shared\Excel5::sizeRow($this->phpSheet, $startRow) / 256;
1022
1023
                    switch ($obj['otObjType']) {
1024
                        case 0x19:
1025
                            // Note
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
1026
//                            echo 'Cell Annotation Object<br />';
1027
//                            echo 'Object ID is ', $obj['idObjID'],'<br />';
1028
                            if (isset($this->cellNotes[$obj['idObjID']])) {
1029
                                $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...
1030
1031 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...
1032
                                    $textObject = $this->textObjects[$obj['idObjID']];
1033
                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
1034
                                }
1035
                            }
1036
                            break;
1037
                        case 0x08:
1038
//                            echo 'Picture Object<br />';
1039
                            // picture
1040
                            // get index to BSE entry (1-based)
1041
                            $BSEindex = $spContainer->getOPT(0x0104);
1042
                            $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...
1043
                            $BSE = $BSECollection[$BSEindex - 1];
1044
                            $blipType = $BSE->getBlipType();
1045
1046
                            // need check because some blip types are not supported by Escher reader such as EMF
1047
                            if ($blip = $BSE->getBlip()) {
1048
                                $ih = imagecreatefromstring($blip->getData());
1049
                                $drawing = new \PhpSpreadsheet\Worksheet\MemoryDrawing();
1050
                                $drawing->setImageResource($ih);
1051
1052
                                // width, height, offsetX, offsetY
1053
                                $drawing->setResizeProportional(false);
1054
                                $drawing->setWidth($width);
1055
                                $drawing->setHeight($height);
1056
                                $drawing->setOffsetX($offsetX);
1057
                                $drawing->setOffsetY($offsetY);
1058
1059
                                switch ($blipType) {
1060
                                    case \PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_JPEG:
1061
                                        $drawing->setRenderingFunction(\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_JPEG);
1062
                                        $drawing->setMimeType(\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_JPEG);
1063
                                        break;
1064
                                    case \PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_PNG:
1065
                                        $drawing->setRenderingFunction(\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_PNG);
1066
                                        $drawing->setMimeType(\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_PNG);
1067
                                        break;
1068
                                }
1069
1070
                                $drawing->setWorksheet($this->phpSheet);
1071
                                $drawing->setCoordinates($spContainer->getStartCoordinates());
1072
                            }
1073
                            break;
1074
                        default:
1075
                            // other object type
1076
                            break;
1077
                    }
1078
                }
1079
            }
1080
1081
            // treat SHAREDFMLA records
1082
            if ($this->version == self::XLS_BIFF8) {
1083
                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
1084
                    list($column, $row) = \PhpSpreadsheet\Cell::coordinateFromString($cell);
1085
                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
1086
                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
1087
                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, \PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
1088
                    }
1089
                }
1090
            }
1091
1092
            if (!empty($this->cellNotes)) {
1093
                foreach ($this->cellNotes as $note => $noteDetails) {
1094
                    if (!isset($noteDetails['objTextData'])) {
1095 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...
1096
                            $textObject = $this->textObjects[$note];
1097
                            $noteDetails['objTextData'] = $textObject;
1098
                        } else {
1099
                            $noteDetails['objTextData']['text'] = '';
1100
                        }
1101
                    }
1102
//                    echo '<b>Cell annotation ', $note,'</b><br />';
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...
1103
//                    var_dump($noteDetails);
1104
//                    echo '<br />';
1105
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
1106
                    $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
1107
                }
1108
            }
1109
        }
1110
1111
        // add the named ranges (defined names)
1112
        foreach ($this->definedname as $definedName) {
1113
            if ($definedName['isBuiltInName']) {
1114
                switch ($definedName['name']) {
1115
                    case pack('C', 0x06):
1116
                        // print area
1117
                        //    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...
1118
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1119
1120
                        $extractedRanges = [];
1121
                        foreach ($ranges as $range) {
1122
                            // $range should look like one of these
1123
                            //        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...
1124
                            //        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...
1125
                            $explodes = explode('!', $range);    // FIXME: what if sheetname contains exclamation mark?
1126
                            $sheetName = trim($explodes[0], "'");
1127
                            if (count($explodes) == 2) {
1128
                                if (strpos($explodes[1], ':') === false) {
1129
                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
1130
                                }
1131
                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
1132
                            }
1133
                        }
1134
                        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...
1135
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
1136
                        }
1137
                        break;
1138
                    case pack('C', 0x07):
1139
                        // print titles (repeating rows)
1140
                        // Assuming BIFF8, there are 3 cases
1141
                        // 1. repeating rows
1142
                        //        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...
1143
                        //        rows 1-2 repeat
1144
                        // 2. repeating columns
1145
                        //        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...
1146
                        //        columns A-B repeat
1147
                        // 3. both repeating rows and repeating columns
1148
                        //        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...
1149
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1150
                        foreach ($ranges as $range) {
1151
                            // $range should look like this one of these
1152
                            //        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...
1153
                            //        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...
1154
                            $explodes = explode('!', $range);
1155
                            if (count($explodes) == 2) {
1156
                                if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
1157
                                    $extractedRange = $explodes[1];
1158
                                    $extractedRange = str_replace('$', '', $extractedRange);
1159
1160
                                    $coordinateStrings = explode(':', $extractedRange);
1161
                                    if (count($coordinateStrings) == 2) {
1162
                                        list($firstColumn, $firstRow) = \PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[0]);
1163
                                        list($lastColumn, $lastRow) = \PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[1]);
1164
1165
                                        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...
1166
                                            // then we have repeating rows
1167
                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
1168
                                        } 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...
1169
                                            // then we have repeating columns
1170
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
1171
                                        }
1172
                                    }
1173
                                }
1174
                            }
1175
                        }
1176
                        break;
1177
                }
1178
            } else {
1179
                // Extract range
1180
                $explodes = explode('!', $definedName['formula']);
1181
1182
                if (count($explodes) == 2) {
1183
                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1184
                        ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $docSheet is correct as $this->spreadsheet->getS...im($explodes[0], '\'')) (which targets PhpSpreadsheet\Spreadsheet::getSheetByName()) seems to always return null.

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

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

}

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

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

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

Loading history...
1185
                        $extractedRange = $explodes[1];
1186
                        $extractedRange = str_replace('$', '', $extractedRange);
1187
1188
                        $localOnly = ($definedName['scope'] == 0) ? false : true;
1189
1190
                        $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
1191
1192
                        $this->spreadsheet->addNamedRange(new \PhpSpreadsheet\NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
0 ignored issues
show
Bug introduced by
It seems like $docSheet can be null; however, __construct() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1193
                    }
1194
                } 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...
1195
                    //    Named Value
1196
                    //    TODO Provide support for named values
1197
                }
1198
            }
1199
        }
1200
        $this->data = null;
1201
1202
        return $this->spreadsheet;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->spreadsheet; (PhpSpreadsheet\Spreadsheet) is incompatible with the return type declared by the interface PhpSpreadsheet\Reader\IReader::load of type PhpSpreadsheet\Reader\PhpSpreadsheet.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
1203
    }
1204
1205
    /**
1206
     * Read record data from stream, decrypting as required
1207
     *
1208
     * @param string $data   Data stream to read from
1209
     * @param int    $pos    Position to start reading from
1210
     * @param int    $length Record data length
0 ignored issues
show
Bug introduced by
There is no parameter named $length. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1211
     *
1212
     * @return string Record data
1213
     */
1214
    private function readRecordData($data, $pos, $len)
1215
    {
1216
        $data = substr($data, $pos, $len);
1217
1218
        // File not encrypted, or record before encryption start point
1219
        if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
1220
            return $data;
1221
        }
1222
1223
        $recordData = '';
1224
        if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
1225
            $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
1226
            $block = floor($pos / self::REKEY_BLOCK);
1227
            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
1228
1229
            // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
1230
            // at a point earlier in the current block, re-use it as we can save some time.
1231
            if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
1232
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1233
                $step = $pos % self::REKEY_BLOCK;
1234
            } else {
1235
                $step = $pos - $this->rc4Pos;
1236
            }
1237
            $this->rc4Key->RC4(str_repeat("\0", $step));
1238
1239
            // Decrypt record data (re-keying at the end of every block)
1240
            while ($block != $endBlock) {
1241
                $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
1242
                $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
1243
                $data = substr($data, $step);
1244
                $pos += $step;
1245
                $len -= $step;
1246
                ++$block;
1247
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1248
            }
1249
            $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
1250
1251
            // Keep track of the position of this decryptor.
1252
            // We'll try and re-use it later if we can to speed things up
1253
            $this->rc4Pos = $pos + $len;
1254
        } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
1255
            throw new Exception('XOr encryption not supported');
1256
        }
1257
1258
        return $recordData;
1259
    }
1260
1261
    /**
1262
     * Use OLE reader to extract the relevant data streams from the OLE file
1263
     *
1264
     * @param string $pFilename
1265
     */
1266
    private function loadOLE($pFilename)
1267
    {
1268
        // OLE reader
1269
        $ole = new \PhpSpreadsheet\Shared\OLERead();
1270
        // get excel data,
1271
        $res = $ole->read($pFilename);
0 ignored issues
show
Unused Code introduced by
$res is not used, you could remove the assignment.

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

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

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

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

Loading history...
Bug introduced by
Are you sure the assignment to $res is correct as $ole->read($pFilename) (which targets PhpSpreadsheet\Shared\OLERead::read()) seems to always return null.

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

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

}

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

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

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

Loading history...
1272
        // Get workbook data: workbook stream + sheet streams
1273
        $this->data = $ole->getStream($ole->wrkbook);
1274
        // Get summary information data
1275
        $this->summaryInformation = $ole->getStream($ole->summaryInformation);
1276
        // Get additional document summary information data
1277
        $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1278
        // Get user-defined property data
1279
//        $this->userDefinedProperties = $ole->getUserDefinedProperties();
1280
    }
1281
1282
    /**
1283
     * Read summary information
1284
     */
1285
    private function readSummaryInformation()
1286
    {
1287
        if (!isset($this->summaryInformation)) {
1288
            return;
1289
        }
1290
1291
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1292
        // 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...
1293
        // offset: 4; size: 2; OS version
1294
        // offset: 6; size: 2; OS indicator
1295
        // offset: 8; size: 16
1296
        // offset: 24; size: 4; section count
1297
        $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...
1298
1299
        // 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
1300
        // offset: 44; size: 4
1301
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1302
1303
        // section header
1304
        // offset: $secOffset; size: 4; section length
1305
        $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...
1306
1307
        // offset: $secOffset+4; size: 4; property count
1308
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1309
1310
        // initialize code page (used to resolve string values)
1311
        $codePage = 'CP1252';
1312
1313
        // 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...
1314
        // loop through property decarations and properties
1315
        for ($i = 0; $i < $countProperties; ++$i) {
1316
            // 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...
1317
            $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
1318
1319
            // Use value of property id as appropriate
1320
            // 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...
1321
            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
1322
1323
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1324
1325
            // initialize property value
1326
            $value = null;
1327
1328
            // extract property value based on property type
1329
            switch ($type) {
1330
                case 0x02: // 2 byte signed integer
1331
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1332
                    break;
1333
                case 0x03: // 4 byte signed integer
1334
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1335
                    break;
1336
                case 0x13: // 4 byte unsigned integer
1337
                    // not needed yet, fix later if necessary
1338
                    break;
1339 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...
1340
                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1341
                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1342
                    $value = \PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1343
                    $value = rtrim($value);
1344
                    break;
1345 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...
1346
                    // PHP-time
1347
                    $value = \PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1348
                    break;
1349
                case 0x47: // Clipboard format
1350
                    // not needed yet, fix later if necessary
1351
                    break;
1352
            }
1353
1354
            switch ($id) {
1355
                case 0x01:    //    Code Page
1356
                    $codePage = \PhpSpreadsheet\Shared\CodePage::numberToName($value);
1357
                    break;
1358
                case 0x02:    //    Title
1359
                    $this->spreadsheet->getProperties()->setTitle($value);
1360
                    break;
1361
                case 0x03:    //    Subject
1362
                    $this->spreadsheet->getProperties()->setSubject($value);
1363
                    break;
1364
                case 0x04:    //    Author (Creator)
1365
                    $this->spreadsheet->getProperties()->setCreator($value);
1366
                    break;
1367
                case 0x05:    //    Keywords
1368
                    $this->spreadsheet->getProperties()->setKeywords($value);
1369
                    break;
1370
                case 0x06:    //    Comments (Description)
1371
                    $this->spreadsheet->getProperties()->setDescription($value);
1372
                    break;
1373
                case 0x07:    //    Template
1374
                    //    Not supported by PhpSpreadsheet
1375
                    break;
1376
                case 0x08:    //    Last Saved By (LastModifiedBy)
1377
                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
1378
                    break;
1379
                case 0x09:    //    Revision
1380
                    //    Not supported by PhpSpreadsheet
1381
                    break;
1382
                case 0x0A:    //    Total Editing Time
1383
                    //    Not supported by PhpSpreadsheet
1384
                    break;
1385
                case 0x0B:    //    Last Printed
1386
                    //    Not supported by PhpSpreadsheet
1387
                    break;
1388
                case 0x0C:    //    Created Date/Time
1389
                    $this->spreadsheet->getProperties()->setCreated($value);
1390
                    break;
1391
                case 0x0D:    //    Modified Date/Time
1392
                    $this->spreadsheet->getProperties()->setModified($value);
1393
                    break;
1394
                case 0x0E:    //    Number of Pages
1395
                    //    Not supported by PhpSpreadsheet
1396
                    break;
1397
                case 0x0F:    //    Number of Words
1398
                    //    Not supported by PhpSpreadsheet
1399
                    break;
1400
                case 0x10:    //    Number of Characters
1401
                    //    Not supported by PhpSpreadsheet
1402
                    break;
1403
                case 0x11:    //    Thumbnail
1404
                    //    Not supported by PhpSpreadsheet
1405
                    break;
1406
                case 0x12:    //    Name of creating application
1407
                    //    Not supported by PhpSpreadsheet
1408
                    break;
1409
                case 0x13:    //    Security
1410
                    //    Not supported by PhpSpreadsheet
1411
                    break;
1412
            }
1413
        }
1414
    }
1415
1416
    /**
1417
     * Read additional document summary information
1418
     */
1419
    private function readDocumentSummaryInformation()
1420
    {
1421
        if (!isset($this->documentSummaryInformation)) {
1422
            return;
1423
        }
1424
1425
        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
1426
        //    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...
1427
        //    offset: 4;    size: 2;    OS version
1428
        //    offset: 6;    size: 2;    OS indicator
1429
        //    offset: 8;    size: 16
1430
        //    offset: 24;    size: 4;    section count
1431
        $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...
1432
//        echo '$secCount = ', $secCount,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1433
1434
        // 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
1435
        // offset: 44;    size: 4;    first section offset
1436
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1437
//        echo '$secOffset = ', $secOffset,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1438
1439
        //    section header
1440
        //    offset: $secOffset;    size: 4;    section length
1441
        $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...
1442
//        echo '$secLength = ', $secLength,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1443
1444
        //    offset: $secOffset+4;    size: 4;    property count
1445
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1446
//        echo '$countProperties = ', $countProperties,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1447
1448
        // initialize code page (used to resolve string values)
1449
        $codePage = 'CP1252';
1450
1451
        //    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...
1452
        //    loop through property decarations and properties
1453
        for ($i = 0; $i < $countProperties; ++$i) {
1454
            //            echo 'Property ', $i,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1455
            //    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...
1456
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
1457
//            echo 'ID is ', $id,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1458
1459
            // Use value of property id as appropriate
1460
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1461
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
1462
1463
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1464
//            echo 'Type is ', $type,', ';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1465
1466
            // initialize property value
1467
            $value = null;
1468
1469
            // extract property value based on property type
1470
            switch ($type) {
1471
                case 0x02:    //    2 byte signed integer
1472
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1473
                    break;
1474
                case 0x03:    //    4 byte signed integer
1475
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1476
                    break;
1477
                case 0x0B:  // Boolean
1478
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1479
                    $value = ($value == 0 ? false : true);
1480
                    break;
1481
                case 0x13:    //    4 byte unsigned integer
1482
                    // not needed yet, fix later if necessary
1483
                    break;
1484 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...
1485
                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1486
                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1487
                    $value = \PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1488
                    $value = rtrim($value);
1489
                    break;
1490 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...
1491
                    // PHP-Time
1492
                    $value = \PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1493
                    break;
1494
                case 0x47:    //    Clipboard format
1495
                    // not needed yet, fix later if necessary
1496
                    break;
1497
            }
1498
1499
            switch ($id) {
1500
                case 0x01:    //    Code Page
1501
                    $codePage = \PhpSpreadsheet\Shared\CodePage::numberToName($value);
1502
                    break;
1503
                case 0x02:    //    Category
1504
                    $this->spreadsheet->getProperties()->setCategory($value);
1505
                    break;
1506
                case 0x03:    //    Presentation Target
1507
                    //    Not supported by PhpSpreadsheet
1508
                    break;
1509
                case 0x04:    //    Bytes
1510
                    //    Not supported by PhpSpreadsheet
1511
                    break;
1512
                case 0x05:    //    Lines
1513
                    //    Not supported by PhpSpreadsheet
1514
                    break;
1515
                case 0x06:    //    Paragraphs
1516
                    //    Not supported by PhpSpreadsheet
1517
                    break;
1518
                case 0x07:    //    Slides
1519
                    //    Not supported by PhpSpreadsheet
1520
                    break;
1521
                case 0x08:    //    Notes
1522
                    //    Not supported by PhpSpreadsheet
1523
                    break;
1524
                case 0x09:    //    Hidden Slides
1525
                    //    Not supported by PhpSpreadsheet
1526
                    break;
1527
                case 0x0A:    //    MM Clips
1528
                    //    Not supported by PhpSpreadsheet
1529
                    break;
1530
                case 0x0B:    //    Scale Crop
1531
                    //    Not supported by PhpSpreadsheet
1532
                    break;
1533
                case 0x0C:    //    Heading Pairs
1534
                    //    Not supported by PhpSpreadsheet
1535
                    break;
1536
                case 0x0D:    //    Titles of Parts
1537
                    //    Not supported by PhpSpreadsheet
1538
                    break;
1539
                case 0x0E:    //    Manager
1540
                    $this->spreadsheet->getProperties()->setManager($value);
1541
                    break;
1542
                case 0x0F:    //    Company
1543
                    $this->spreadsheet->getProperties()->setCompany($value);
1544
                    break;
1545
                case 0x10:    //    Links up-to-date
1546
                    //    Not supported by PhpSpreadsheet
1547
                    break;
1548
            }
1549
        }
1550
    }
1551
1552
    /**
1553
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1554
     */
1555
    private function readDefault()
1556
    {
1557
        $length = self::getInt2d($this->data, $this->pos + 2);
1558
//        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
1559
1560
        // move stream pointer to next record
1561
        $this->pos += 4 + $length;
1562
    }
1563
1564
    /**
1565
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1566
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1567
     */
1568
    private function readNote()
1569
    {
1570
        //        echo '<b>Read Cell Annotation</b><br />';
1571
        $length = self::getInt2d($this->data, $this->pos + 2);
1572
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1573
1574
        // move stream pointer to next record
1575
        $this->pos += 4 + $length;
1576
1577
        if ($this->readDataOnly) {
1578
            return;
1579
        }
1580
1581
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1582
        if ($this->version == self::XLS_BIFF8) {
1583
            $noteObjID = self::getInt2d($recordData, 6);
1584
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1585
            $noteAuthor = $noteAuthor['value'];
1586
//            echo 'Note Address=', $cellAddress,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% 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...
1587
//            echo 'Note Object ID=', $noteObjID,'<br />';
1588
//            echo 'Note Author=', $noteAuthor,'<hr />';
1589
//
1590
            $this->cellNotes[$noteObjID] = [
1591
                'cellRef' => $cellAddress,
1592
                'objectID' => $noteObjID,
1593
                'author' => $noteAuthor,
1594
            ];
1595
        } else {
1596
            $extension = false;
1597
            if ($cellAddress == '$B$65536') {
1598
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1599
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1600
                //        max 2048 bytes will probably throw a wobbly.
1601
                $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...
1602
                $extension = true;
1603
                $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...
1604
            }
1605
//            echo 'Note Address=', $cellAddress,'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
1606
1607
            $cellAddress = str_replace('$', '', $cellAddress);
1608
            $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...
1609
            $noteText = trim(substr($recordData, 6));
1610
//            echo 'Note Length=', $noteLength,'<br />';
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...
1611
//            echo 'Note Text=', $noteText,'<br />';
1612
1613
            if ($extension) {
1614
                //    Concatenate this extension with the currently set comment for the cell
1615
                $comment = $this->phpSheet->getComment($cellAddress);
1616
                $commentText = $comment->getText()->getPlainText();
1617
                $comment->setText($this->parseRichText($commentText . $noteText));
1618
            } else {
1619
                //    Set comment for the cell
1620
                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
1621
//                                                    ->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...
1622
            }
1623
        }
1624
    }
1625
1626
    /**
1627
     *    The TEXT Object record contains the text associated with a cell annotation.
1628
     */
1629
    private function readTextObject()
1630
    {
1631
        $length = self::getInt2d($this->data, $this->pos + 2);
1632
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1633
1634
        // move stream pointer to next record
1635
        $this->pos += 4 + $length;
1636
1637
        if ($this->readDataOnly) {
1638
            return;
1639
        }
1640
1641
        // recordData consists of an array of subrecords looking like this:
1642
        //    grbit: 2 bytes; Option Flags
1643
        //    rot: 2 bytes; rotation
1644
        //    cchText: 2 bytes; length of the text (in the first continue record)
1645
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1646
        // followed by the continuation records containing the actual text and formatting
1647
        $grbitOpts = self::getInt2d($recordData, 0);
1648
        $rot = self::getInt2d($recordData, 2);
1649
        $cchText = self::getInt2d($recordData, 10);
1650
        $cbRuns = self::getInt2d($recordData, 12);
1651
        $text = $this->getSplicedRecordData();
1652
1653
        $this->textObjects[$this->textObjRef] = [
1654
            'text' => substr($text['recordData'], $text['spliceOffsets'][0] + 1, $cchText),
1655
            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
1656
            'alignment' => $grbitOpts,
1657
            'rotation' => $rot,
1658
        ];
1659
1660
//        echo '<b>_readTextObject()</b><br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
1661
//        var_dump($this->textObjects[$this->textObjRef]);
1662
//        echo '<br />';
1663
    }
1664
1665
    /**
1666
     * Read BOF
1667
     */
1668
    private function readBof()
1669
    {
1670
        $length = self::getInt2d($this->data, $this->pos + 2);
1671
        $recordData = substr($this->data, $this->pos + 4, $length);
1672
1673
        // move stream pointer to next record
1674
        $this->pos += 4 + $length;
1675
1676
        // offset: 2; size: 2; type of the following data
1677
        $substreamType = self::getInt2d($recordData, 2);
1678
1679
        switch ($substreamType) {
1680
            case self::XLS_WORKBOOKGLOBALS:
1681
                $version = self::getInt2d($recordData, 0);
1682
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1683
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1684
                }
1685
                $this->version = $version;
1686
                break;
1687
            case self::XLS_WORKSHEET:
1688
                // do not use this version information for anything
1689
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1690
                break;
1691
            default:
1692
                // substream, e.g. chart
1693
                // just skip the entire substream
1694
                do {
1695
                    $code = self::getInt2d($this->data, $this->pos);
1696
                    $this->readDefault();
1697
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1698
                break;
1699
        }
1700
    }
1701
1702
    /**
1703
     * FILEPASS
1704
     *
1705
     * This record is part of the File Protection Block. It
1706
     * contains information about the read/write password of the
1707
     * file. All record contents following this record will be
1708
     * encrypted.
1709
     *
1710
     * --    "OpenOffice.org's Documentation of the Microsoft
1711
     *         Excel File Format"
1712
     *
1713
     * The decryption functions and objects used from here on in
1714
     * are based on the source of Spreadsheet-ParseExcel:
1715
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1716
     */
1717
    private function readFilepass()
1718
    {
1719
        $length = self::getInt2d($this->data, $this->pos + 2);
1720
1721
        if ($length != 54) {
1722
            throw new Exception('Unexpected file pass record length');
1723
        }
1724
1725
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1726
1727
        // move stream pointer to next record
1728
        $this->pos += 4 + $length;
1729
1730
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1731
            throw new Exception('Decryption password incorrect');
1732
        }
1733
1734
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1735
1736
        // Decryption required from the record after next onwards
1737
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1738
    }
1739
1740
    /**
1741
     * Make an RC4 decryptor for the given block
1742
     *
1743
     * @var int         Block for which to create decrypto
1744
     * @var string $valContext MD5 context state
1745
     *
1746
     * @return Excel5\RC4
1747
     */
1748
    private function makeKey($block, $valContext)
1749
    {
1750
        $pwarray = str_repeat("\0", 64);
1751
1752
        for ($i = 0; $i < 5; ++$i) {
1753
            $pwarray[$i] = $valContext[$i];
1754
        }
1755
1756
        $pwarray[5] = chr($block & 0xff);
1757
        $pwarray[6] = chr(($block >> 8) & 0xff);
1758
        $pwarray[7] = chr(($block >> 16) & 0xff);
1759
        $pwarray[8] = chr(($block >> 24) & 0xff);
1760
1761
        $pwarray[9] = "\x80";
1762
        $pwarray[56] = "\x48";
1763
1764
        $md5 = new Excel5\MD5();
1765
        $md5->add($pwarray);
1766
1767
        $s = $md5->getContext();
1768
1769
        return new Excel5\RC4($s);
1770
    }
1771
1772
    /**
1773
     * Verify RC4 file password
1774
     *
1775
     * @var string        Password to check
1776
     * @var string $docid           Document id
1777
     * @var string $salt_data       Salt data
1778
     * @var string $hashedsalt_data Hashed salt data
1779
     * @var string &$valContext     Set to the MD5 context of the value
1780
     *
1781
     * @return bool Success
1782
     */
1783
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1784
    {
1785
        $pwarray = str_repeat("\0", 64);
1786
1787
        for ($i = 0; $i < strlen($password); ++$i) {
1788
            $o = ord(substr($password, $i, 1));
1789
            $pwarray[2 * $i] = chr($o & 0xff);
1790
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1791
        }
1792
        $pwarray[2 * $i] = chr(0x80);
1793
        $pwarray[56] = chr(($i << 4) & 0xff);
1794
1795
        $md5 = new Excel5\MD5();
1796
        $md5->add($pwarray);
1797
1798
        $mdContext1 = $md5->getContext();
1799
1800
        $offset = 0;
1801
        $keyoffset = 0;
1802
        $tocopy = 5;
1803
1804
        $md5->reset();
1805
1806
        while ($offset != 16) {
1807
            if ((64 - $offset) < 5) {
1808
                $tocopy = 64 - $offset;
1809
            }
1810
            for ($i = 0; $i <= $tocopy; ++$i) {
1811
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1812
            }
1813
            $offset += $tocopy;
1814
1815
            if ($offset == 64) {
1816
                $md5->add($pwarray);
1817
                $keyoffset = $tocopy;
1818
                $tocopy = 5 - $tocopy;
1819
                $offset = 0;
1820
                continue;
1821
            }
1822
1823
            $keyoffset = 0;
1824
            $tocopy = 5;
1825
            for ($i = 0; $i < 16; ++$i) {
1826
                $pwarray[$offset + $i] = $docid[$i];
1827
            }
1828
            $offset += 16;
1829
        }
1830
1831
        $pwarray[16] = "\x80";
1832
        for ($i = 0; $i < 47; ++$i) {
1833
            $pwarray[17 + $i] = "\0";
1834
        }
1835
        $pwarray[56] = "\x80";
1836
        $pwarray[57] = "\x0a";
1837
1838
        $md5->add($pwarray);
1839
        $valContext = $md5->getContext();
1840
1841
        $key = $this->makeKey(0, $valContext);
1842
1843
        $salt = $key->RC4($salt_data);
1844
        $hashedsalt = $key->RC4($hashedsalt_data);
1845
1846
        $salt .= "\x80" . str_repeat("\0", 47);
1847
        $salt[56] = "\x80";
1848
1849
        $md5->reset();
1850
        $md5->add($salt);
1851
        $mdContext2 = $md5->getContext();
1852
1853
        return $mdContext2 == $hashedsalt;
1854
    }
1855
1856
    /**
1857
     * CODEPAGE
1858
     *
1859
     * This record stores the text encoding used to write byte
1860
     * strings, stored as MS Windows code page identifier.
1861
     *
1862
     * --    "OpenOffice.org's Documentation of the Microsoft
1863
     *         Excel File Format"
1864
     */
1865 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...
1866
    {
1867
        $length = self::getInt2d($this->data, $this->pos + 2);
1868
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1869
1870
        // move stream pointer to next record
1871
        $this->pos += 4 + $length;
1872
1873
        // offset: 0; size: 2; code page identifier
1874
        $codepage = self::getInt2d($recordData, 0);
1875
1876
        $this->codepage = \PhpSpreadsheet\Shared\CodePage::numberToName($codepage);
1877
    }
1878
1879
    /**
1880
     * DATEMODE
1881
     *
1882
     * This record specifies the base date for displaying date
1883
     * values. All dates are stored as count of days past this
1884
     * base date. In BIFF2-BIFF4 this record is part of the
1885
     * Calculation Settings Block. In BIFF5-BIFF8 it is
1886
     * stored in the Workbook Globals Substream.
1887
     *
1888
     * --    "OpenOffice.org's Documentation of the Microsoft
1889
     *         Excel File Format"
1890
     */
1891
    private function readDateMode()
1892
    {
1893
        $length = self::getInt2d($this->data, $this->pos + 2);
1894
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1895
1896
        // move stream pointer to next record
1897
        $this->pos += 4 + $length;
1898
1899
        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
1900
        \PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900);
1901
        if (ord($recordData{0}) == 1) {
1902
            \PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpSpreadsheet\Shared\Date::CALENDAR_MAC_1904);
1903
        }
1904
    }
1905
1906
    /**
1907
     * Read a FONT record
1908
     */
1909
    private function readFont()
1910
    {
1911
        $length = self::getInt2d($this->data, $this->pos + 2);
1912
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1913
1914
        // move stream pointer to next record
1915
        $this->pos += 4 + $length;
1916
1917
        if (!$this->readDataOnly) {
1918
            $objFont = new \PhpSpreadsheet\Style\Font();
1919
1920
            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
1921
            $size = self::getInt2d($recordData, 0);
1922
            $objFont->setSize($size / 20);
1923
1924
            // offset: 2; size: 2; option flags
1925
            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
1926
            // bit: 1; mask 0x0002; italic
1927
            $isItalic = (0x0002 & self::getInt2d($recordData, 2)) >> 1;
1928
            if ($isItalic) {
1929
                $objFont->setItalic(true);
1930
            }
1931
1932
            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
1933
            // bit: 3; mask 0x0008; strike
1934
            $isStrike = (0x0008 & self::getInt2d($recordData, 2)) >> 3;
1935
            if ($isStrike) {
1936
                $objFont->setStrikethrough(true);
1937
            }
1938
1939
            // offset: 4; size: 2; colour index
1940
            $colorIndex = self::getInt2d($recordData, 4);
1941
            $objFont->colorIndex = $colorIndex;
0 ignored issues
show
Bug introduced by
The property colorIndex does not seem to exist in PhpSpreadsheet\Style\Font.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
2199
2200
                // bit: 29-23; mask: 0x3F800000; right color
2201
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2202
2203
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2204
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2205
2206
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2207
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2208
2209
                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...
2210
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_NONE);
2211
                } 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...
2212
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_UP);
2213
                } 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...
2214
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_DOWN);
2215
                } 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...
2216
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_BOTH);
2217
                }
2218
2219
                // 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...
2220
                // bit: 6-0; mask: 0x0000007F; top color
2221
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2222
2223
                // bit: 13-7; mask: 0x00003F80; bottom color
2224
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2225
2226
                // bit: 20-14; mask: 0x001FC000; diagonal color
2227
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2228
2229
                // bit: 24-21; mask: 0x01E00000; diagonal style
2230 View Code Duplication
                if ($bordersDiagonalStyle = Excel5\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2231
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2232
                }
2233
2234
                // bit: 31-26; mask: 0xFC000000 fill pattern
2235
                if ($fillType = Excel5\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2236
                    $objStyle->getFill()->setFillType($fillType);
2237
                }
2238
                // offset: 18; size: 2; pattern and background colour
2239
                // bit: 6-0; mask: 0x007F; color index for pattern color
2240
                $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...
2241
2242
                // bit: 13-7; mask: 0x3F80; color index for pattern background
2243
                $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...
2244
            } else {
2245
                // BIFF5
2246
2247
                // offset: 7; size: 1; Text orientation and flags
2248
                $orientationAndFlags = ord($recordData{7});
2249
2250
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2251
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2252
                switch ($xfOrientation) {
2253
                    case 0:
2254
                        $objStyle->getAlignment()->setTextRotation(0);
2255
                        break;
2256
                    case 1:
2257
                        $objStyle->getAlignment()->setTextRotation(-165);
2258
                        break;
2259
                    case 2:
2260
                        $objStyle->getAlignment()->setTextRotation(90);
2261
                        break;
2262
                    case 3:
2263
                        $objStyle->getAlignment()->setTextRotation(-90);
2264
                        break;
2265
                }
2266
2267
                // offset: 8; size: 4; cell border lines and background area
2268
                $borderAndBackground = self::getInt4d($recordData, 8);
2269
2270
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2271
                $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...
2272
2273
                // bit: 13-7; mask: 0x00003F80; color index for pattern background
2274
                $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...
2275
2276
                // bit: 21-16; mask: 0x003F0000; fill pattern
2277
                $objStyle->getFill()->setFillType(Excel5\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2278
2279
                // bit: 24-22; mask: 0x01C00000; bottom line style
2280
                $objStyle->getBorders()->getBottom()->setBorderStyle(Excel5\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2281
2282
                // bit: 31-25; mask: 0xFE000000; bottom line color
2283
                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2284
2285
                // offset: 12; size: 4; cell border lines
2286
                $borderLines = self::getInt4d($recordData, 12);
2287
2288
                // bit: 2-0; mask: 0x00000007; top line style
2289
                $objStyle->getBorders()->getTop()->setBorderStyle(Excel5\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2290
2291
                // bit: 5-3; mask: 0x00000038; left line style
2292
                $objStyle->getBorders()->getLeft()->setBorderStyle(Excel5\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2293
2294
                // bit: 8-6; mask: 0x000001C0; right line style
2295
                $objStyle->getBorders()->getRight()->setBorderStyle(Excel5\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2296
2297
                // bit: 15-9; mask: 0x0000FE00; top line color index
2298
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2299
2300
                // bit: 22-16; mask: 0x007F0000; left line color index
2301
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2302
2303
                // bit: 29-23; mask: 0x3F800000; right line color index
2304
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2305
            }
2306
2307
            // add cellStyleXf or cellXf and update mapping
2308
            if ($isCellStyleXf) {
2309
                // we only read one style XF record which is always the first
2310
                if ($this->xfIndex == 0) {
2311
                    $this->spreadsheet->addCellStyleXf($objStyle);
2312
                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
2313
                }
2314
            } else {
2315
                // we read all cell XF records
2316
                $this->spreadsheet->addCellXf($objStyle);
2317
                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2318
            }
2319
2320
            // update XF index for when we read next record
2321
            ++$this->xfIndex;
2322
        }
2323
    }
2324
2325
    /**
2326
     *
2327
     */
2328
    private function readXfExt()
2329
    {
2330
        $length = self::getInt2d($this->data, $this->pos + 2);
2331
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2332
2333
        // move stream pointer to next record
2334
        $this->pos += 4 + $length;
2335
2336
        if (!$this->readDataOnly) {
2337
            // offset: 0; size: 2; 0x087D = repeated header
2338
2339
            // offset: 2; size: 2
2340
2341
            // offset: 4; size: 8; not used
2342
2343
            // offset: 12; size: 2; record version
2344
2345
            // offset: 14; size: 2; index to XF record which this record modifies
2346
            $ixfe = self::getInt2d($recordData, 14);
2347
2348
            // offset: 16; size: 2; not used
2349
2350
            // offset: 18; size: 2; number of extension properties that follow
2351
            $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...
2352
2353
            // start reading the actual extension data
2354
            $offset = 20;
2355
            while ($offset < $length) {
2356
                // extension type
2357
                $extType = self::getInt2d($recordData, $offset);
2358
2359
                // extension length
2360
                $cb = self::getInt2d($recordData, $offset + 2);
2361
2362
                // extension data
2363
                $extData = substr($recordData, $offset + 4, $cb);
2364
2365
                switch ($extType) {
2366 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...
2367
                        $xclfType = self::getInt2d($extData, 0); // color type
2368
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2369
2370
                        if ($xclfType == 2) {
2371
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2372
2373
                            // modify the relevant style property
2374
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2375
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2376
                                $fill->getStartColor()->setRGB($rgb);
2377
                                unset($fill->startcolorIndex); // normal color index does not apply, discard
2378
                            }
2379
                        }
2380
                        break;
2381 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...
2382
                        $xclfType = self::getInt2d($extData, 0); // color type
2383
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2384
2385
                        if ($xclfType == 2) {
2386
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2387
2388
                            // modify the relevant style property
2389
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2390
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2391
                                $fill->getEndColor()->setRGB($rgb);
2392
                                unset($fill->endcolorIndex); // normal color index does not apply, discard
2393
                            }
2394
                        }
2395
                        break;
2396 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...
2397
                        $xclfType = self::getInt2d($extData, 0); // color type
2398
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2399
2400
                        if ($xclfType == 2) {
2401
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2402
2403
                            // modify the relevant style property
2404
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2405
                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2406
                                $top->getColor()->setRGB($rgb);
2407
                                unset($top->colorIndex); // normal color index does not apply, discard
2408
                            }
2409
                        }
2410
                        break;
2411 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...
2412
                        $xclfType = self::getInt2d($extData, 0); // color type
2413
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2414
2415
                        if ($xclfType == 2) {
2416
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2417
2418
                            // modify the relevant style property
2419
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2420
                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2421
                                $bottom->getColor()->setRGB($rgb);
2422
                                unset($bottom->colorIndex); // normal color index does not apply, discard
2423
                            }
2424
                        }
2425
                        break;
2426 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...
2427
                        $xclfType = self::getInt2d($extData, 0); // color type
2428
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2429
2430
                        if ($xclfType == 2) {
2431
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2432
2433
                            // modify the relevant style property
2434
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2435
                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2436
                                $left->getColor()->setRGB($rgb);
2437
                                unset($left->colorIndex); // normal color index does not apply, discard
2438
                            }
2439
                        }
2440
                        break;
2441 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...
2442
                        $xclfType = self::getInt2d($extData, 0); // color type
2443
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2444
2445
                        if ($xclfType == 2) {
2446
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2447
2448
                            // modify the relevant style property
2449
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2450
                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2451
                                $right->getColor()->setRGB($rgb);
2452
                                unset($right->colorIndex); // normal color index does not apply, discard
2453
                            }
2454
                        }
2455
                        break;
2456 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...
2457
                        $xclfType = self::getInt2d($extData, 0); // color type
2458
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2459
2460
                        if ($xclfType == 2) {
2461
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2462
2463
                            // modify the relevant style property
2464
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2465
                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2466
                                $diagonal->getColor()->setRGB($rgb);
2467
                                unset($diagonal->colorIndex); // normal color index does not apply, discard
2468
                            }
2469
                        }
2470
                        break;
2471 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...
2472
                        $xclfType = self::getInt2d($extData, 0); // color type
2473
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2474
2475
                        if ($xclfType == 2) {
2476
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2477
2478
                            // modify the relevant style property
2479
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2480
                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2481
                                $font->getColor()->setRGB($rgb);
2482
                                unset($font->colorIndex); // normal color index does not apply, discard
2483
                            }
2484
                        }
2485
                        break;
2486
                }
2487
2488
                $offset += $cb;
2489
            }
2490
        }
2491
    }
2492
2493
    /**
2494
     * Read STYLE record
2495
     */
2496
    private function readStyle()
2497
    {
2498
        $length = self::getInt2d($this->data, $this->pos + 2);
2499
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2500
2501
        // move stream pointer to next record
2502
        $this->pos += 4 + $length;
2503
2504
        if (!$this->readDataOnly) {
2505
            // offset: 0; size: 2; index to XF record and flag for built-in style
2506
            $ixfe = self::getInt2d($recordData, 0);
2507
2508
            // bit: 11-0; mask 0x0FFF; index to XF record
2509
            $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...
2510
2511
            // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
2512
            $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
2513
2514
            if ($isBuiltIn) {
2515
                // offset: 2; size: 1; identifier for built-in style
2516
                $builtInId = ord($recordData{2});
2517
2518
                switch ($builtInId) {
2519
                    case 0x00:
2520
                        // currently, we are not using this for anything
2521
                        break;
2522
                    default:
2523
                        break;
2524
                }
2525
            } 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...
2526
                // user-defined; not supported by PhpSpreadsheet
2527
            }
2528
        }
2529
    }
2530
2531
    /**
2532
     * Read PALETTE record
2533
     */
2534
    private function readPalette()
2535
    {
2536
        $length = self::getInt2d($this->data, $this->pos + 2);
2537
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2538
2539
        // move stream pointer to next record
2540
        $this->pos += 4 + $length;
2541
2542
        if (!$this->readDataOnly) {
2543
            // offset: 0; size: 2; number of following colors
2544
            $nm = self::getInt2d($recordData, 0);
2545
2546
            // list of RGB colors
2547
            for ($i = 0; $i < $nm; ++$i) {
2548
                $rgb = substr($recordData, 2 + 4 * $i, 4);
2549
                $this->palette[] = self::readRGB($rgb);
2550
            }
2551
        }
2552
    }
2553
2554
    /**
2555
     * SHEET
2556
     *
2557
     * This record is  located in the  Workbook Globals
2558
     * Substream  and represents a sheet inside the workbook.
2559
     * One SHEET record is written for each sheet. It stores the
2560
     * sheet name and a stream offset to the BOF record of the
2561
     * respective Sheet Substream within the Workbook Stream.
2562
     *
2563
     * --    "OpenOffice.org's Documentation of the Microsoft
2564
     *         Excel File Format"
2565
     */
2566
    private function readSheet()
2567
    {
2568
        $length = self::getInt2d($this->data, $this->pos + 2);
2569
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2570
2571
        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
2572
        // NOTE: not encrypted
2573
        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
2574
2575
        // move stream pointer to next record
2576
        $this->pos += 4 + $length;
2577
2578
        // offset: 4; size: 1; sheet state
2579
        switch (ord($recordData{4})) {
2580
            case 0x00:
2581
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2582
                break;
2583
            case 0x01:
2584
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_HIDDEN;
2585
                break;
2586
            case 0x02:
2587
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VERYHIDDEN;
2588
                break;
2589
            default:
2590
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2591
                break;
2592
        }
2593
2594
        // offset: 5; size: 1; sheet type
2595
        $sheetType = ord($recordData{5});
2596
2597
        // offset: 6; size: var; sheet name
2598 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...
2599
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2600
            $rec_name = $string['value'];
2601
        } elseif ($this->version == self::XLS_BIFF7) {
2602
            $string = $this->readByteStringShort(substr($recordData, 6));
2603
            $rec_name = $string['value'];
2604
        }
2605
2606
        $this->sheets[] = [
2607
            '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...
2608
            'offset' => $rec_offset,
2609
            'sheetState' => $sheetState,
2610
            'sheetType' => $sheetType,
2611
        ];
2612
    }
2613
2614
    /**
2615
     * Read EXTERNALBOOK record
2616
     */
2617
    private function readExternalBook()
2618
    {
2619
        $length = self::getInt2d($this->data, $this->pos + 2);
2620
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2621
2622
        // move stream pointer to next record
2623
        $this->pos += 4 + $length;
2624
2625
        // offset within record data
2626
        $offset = 0;
2627
2628
        // there are 4 types of records
2629
        if (strlen($recordData) > 4) {
2630
            // external reference
2631
            // offset: 0; size: 2; number of sheet names ($nm)
2632
            $nm = self::getInt2d($recordData, 0);
2633
            $offset += 2;
2634
2635
            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
2636
            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2637
            $offset += $encodedUrlString['size'];
2638
2639
            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
2640
            $externalSheetNames = [];
2641
            for ($i = 0; $i < $nm; ++$i) {
2642
                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2643
                $externalSheetNames[] = $externalSheetNameString['value'];
2644
                $offset += $externalSheetNameString['size'];
2645
            }
2646
2647
            // store the record data
2648
            $this->externalBooks[] = [
2649
                'type' => 'external',
2650
                'encodedUrl' => $encodedUrlString['value'],
2651
                'externalSheetNames' => $externalSheetNames,
2652
            ];
2653
        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
2654
            // internal reference
2655
            // offset: 0; size: 2; number of sheet in this document
2656
            // 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...
2657
            $this->externalBooks[] = [
2658
                'type' => 'internal',
2659
            ];
2660
        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
2661
            // add-in function
2662
            // 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...
2663
            $this->externalBooks[] = [
2664
                'type' => 'addInFunction',
2665
            ];
2666
        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
2667
            // DDE links, OLE links
2668
            // 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...
2669
            // offset: 2; size: var; encoded source document name
2670
            $this->externalBooks[] = [
2671
                'type' => 'DDEorOLE',
2672
            ];
2673
        }
2674
    }
2675
2676
    /**
2677
     * Read EXTERNNAME record.
2678
     */
2679
    private function readExternName()
2680
    {
2681
        $length = self::getInt2d($this->data, $this->pos + 2);
2682
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2683
2684
        // move stream pointer to next record
2685
        $this->pos += 4 + $length;
2686
2687
        // external sheet references provided for named cells
2688
        if ($this->version == self::XLS_BIFF8) {
2689
            // offset: 0; size: 2; options
2690
            $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...
2691
2692
            // 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...
2693
2694
            // offset: 4; size: 2; not used
2695
2696
            // offset: 6; size: var
2697
            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2698
2699
            // offset: var; size: var; formula data
2700
            $offset = 6 + $nameString['size'];
2701
            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
2702
2703
            $this->externalNames[] = [
2704
                'name' => $nameString['value'],
2705
                'formula' => $formula,
2706
            ];
2707
        }
2708
    }
2709
2710
    /**
2711
     * Read EXTERNSHEET record
2712
     */
2713
    private function readExternSheet()
2714
    {
2715
        $length = self::getInt2d($this->data, $this->pos + 2);
2716
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2717
2718
        // move stream pointer to next record
2719
        $this->pos += 4 + $length;
2720
2721
        // external sheet references provided for named cells
2722
        if ($this->version == self::XLS_BIFF8) {
2723
            // offset: 0; size: 2; number of following ref structures
2724
            $nm = self::getInt2d($recordData, 0);
2725
            for ($i = 0; $i < $nm; ++$i) {
2726
                $this->ref[] = [
2727
                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
2728
                    'externalBookIndex' => self::getInt2d($recordData, 2 + 6 * $i),
2729
                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
2730
                    'firstSheetIndex' => self::getInt2d($recordData, 4 + 6 * $i),
2731
                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
2732
                    'lastSheetIndex' => self::getInt2d($recordData, 6 + 6 * $i),
2733
                ];
2734
            }
2735
        }
2736
    }
2737
2738
    /**
2739
     * DEFINEDNAME
2740
     *
2741
     * This record is part of a Link Table. It contains the name
2742
     * and the token array of an internal defined name. Token
2743
     * arrays of defined names contain tokens with aberrant
2744
     * token classes.
2745
     *
2746
     * --    "OpenOffice.org's Documentation of the Microsoft
2747
     *         Excel File Format"
2748
     */
2749
    private function readDefinedName()
2750
    {
2751
        $length = self::getInt2d($this->data, $this->pos + 2);
2752
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2753
2754
        // move stream pointer to next record
2755
        $this->pos += 4 + $length;
2756
2757
        if ($this->version == self::XLS_BIFF8) {
2758
            // retrieves named cells
2759
2760
            // offset: 0; size: 2; option flags
2761
            $opts = self::getInt2d($recordData, 0);
2762
2763
            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
2764
            $isBuiltInName = (0x0020 & $opts) >> 5;
2765
2766
            // offset: 2; size: 1; keyboard shortcut
2767
2768
            // offset: 3; size: 1; length of the name (character count)
2769
            $nlen = ord($recordData{3});
2770
2771
            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
2772
            // note: there can also be additional data, this is not included in $flen
2773
            $flen = self::getInt2d($recordData, 4);
2774
2775
            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
2776
            $scope = self::getInt2d($recordData, 8);
2777
2778
            // offset: 14; size: var; Name (Unicode string without length field)
2779
            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2780
2781
            // offset: var; size: $flen; formula data
2782
            $offset = 14 + $string['size'];
2783
            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
2784
2785
            try {
2786
                $formula = $this->getFormulaFromStructure($formulaStructure);
2787
            } catch (\PhpSpreadsheet\Exception $e) {
2788
                $formula = '';
2789
            }
2790
2791
            $this->definedname[] = [
2792
                'isBuiltInName' => $isBuiltInName,
2793
                'name' => $string['value'],
2794
                'formula' => $formula,
2795
                'scope' => $scope,
2796
            ];
2797
        }
2798
    }
2799
2800
    /**
2801
     * Read MSODRAWINGGROUP record
2802
     */
2803 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...
2804
    {
2805
        $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...
2806
2807
        // get spliced record data
2808
        $splicedRecordData = $this->getSplicedRecordData();
2809
        $recordData = $splicedRecordData['recordData'];
2810
2811
        $this->drawingGroupData .= $recordData;
2812
    }
2813
2814
    /**
2815
     * SST - Shared String Table
2816
     *
2817
     * This record contains a list of all strings used anywhere
2818
     * in the workbook. Each string occurs only once. The
2819
     * workbook uses indexes into the list to reference the
2820
     * strings.
2821
     *
2822
     * --    "OpenOffice.org's Documentation of the Microsoft
2823
     *         Excel File Format"
2824
     **/
2825
    private function readSst()
2826
    {
2827
        // offset within (spliced) record data
2828
        $pos = 0;
2829
2830
        // get spliced record data
2831
        $splicedRecordData = $this->getSplicedRecordData();
2832
2833
        $recordData = $splicedRecordData['recordData'];
2834
        $spliceOffsets = $splicedRecordData['spliceOffsets'];
2835
2836
        // offset: 0; size: 4; total number of strings in the workbook
2837
        $pos += 4;
2838
2839
        // offset: 4; size: 4; number of following strings ($nm)
2840
        $nm = self::getInt4d($recordData, 4);
2841
        $pos += 4;
2842
2843
        // loop through the Unicode strings (16-bit length)
2844
        for ($i = 0; $i < $nm; ++$i) {
2845
            // number of characters in the Unicode string
2846
            $numChars = self::getInt2d($recordData, $pos);
2847
            $pos += 2;
2848
2849
            // option flags
2850
            $optionFlags = ord($recordData{$pos});
2851
            ++$pos;
2852
2853
            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
2854
            $isCompressed = (($optionFlags & 0x01) == 0);
2855
2856
            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
2857
            $hasAsian = (($optionFlags & 0x04) != 0);
2858
2859
            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
2860
            $hasRichText = (($optionFlags & 0x08) != 0);
2861
2862
            if ($hasRichText) {
2863
                // number of Rich-Text formatting runs
2864
                $formattingRuns = self::getInt2d($recordData, $pos);
2865
                $pos += 2;
2866
            }
2867
2868
            if ($hasAsian) {
2869
                // size of Asian phonetic setting
2870
                $extendedRunLength = self::getInt4d($recordData, $pos);
2871
                $pos += 4;
2872
            }
2873
2874
            // expected byte length of character array if not split
2875
            $len = ($isCompressed) ? $numChars : $numChars * 2;
2876
2877
            // look up limit position
2878
            foreach ($spliceOffsets as $spliceOffset) {
2879
                // it can happen that the string is empty, therefore we need
2880
                // <= and not just <
2881
                if ($pos <= $spliceOffset) {
2882
                    $limitpos = $spliceOffset;
2883
                    break;
2884
                }
2885
            }
2886
2887
            if ($pos + $len <= $limitpos) {
2888
                // character array is not split between records
2889
2890
                $retstr = substr($recordData, $pos, $len);
2891
                $pos += $len;
2892
            } else {
2893
                // character array is split between records
2894
2895
                // first part of character array
2896
                $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...
2897
2898
                $bytesRead = $limitpos - $pos;
2899
2900
                // remaining characters in Unicode string
2901
                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2902
2903
                $pos = $limitpos;
2904
2905
                // keep reading the characters
2906
                while ($charsLeft > 0) {
2907
                    // look up next limit position, in case the string span more than one continue record
2908
                    foreach ($spliceOffsets as $spliceOffset) {
2909
                        if ($pos < $spliceOffset) {
2910
                            $limitpos = $spliceOffset;
2911
                            break;
2912
                        }
2913
                    }
2914
2915
                    // repeated option flags
2916
                    // OpenOffice.org documentation 5.21
2917
                    $option = ord($recordData{$pos});
2918
                    ++$pos;
2919
2920
                    if ($isCompressed && ($option == 0)) {
2921
                        // 1st fragment compressed
2922
                        // this fragment compressed
2923
                        $len = min($charsLeft, $limitpos - $pos);
2924
                        $retstr .= substr($recordData, $pos, $len);
2925
                        $charsLeft -= $len;
2926
                        $isCompressed = true;
2927
                    } elseif (!$isCompressed && ($option != 0)) {
2928
                        // 1st fragment uncompressed
2929
                        // this fragment uncompressed
2930
                        $len = min($charsLeft * 2, $limitpos - $pos);
2931
                        $retstr .= substr($recordData, $pos, $len);
2932
                        $charsLeft -= $len / 2;
2933
                        $isCompressed = false;
2934
                    } elseif (!$isCompressed && ($option == 0)) {
2935
                        // 1st fragment uncompressed
2936
                        // this fragment compressed
2937
                        $len = min($charsLeft, $limitpos - $pos);
2938
                        for ($j = 0; $j < $len; ++$j) {
2939
                            $retstr .= $recordData{$pos + $j}
2940
                            . chr(0);
2941
                        }
2942
                        $charsLeft -= $len;
2943
                        $isCompressed = false;
2944
                    } else {
2945
                        // 1st fragment compressed
2946
                        // this fragment uncompressed
2947
                        $newstr = '';
2948
                        for ($j = 0; $j < strlen($retstr); ++$j) {
2949
                            $newstr .= $retstr[$j] . chr(0);
2950
                        }
2951
                        $retstr = $newstr;
2952
                        $len = min($charsLeft * 2, $limitpos - $pos);
2953
                        $retstr .= substr($recordData, $pos, $len);
2954
                        $charsLeft -= $len / 2;
2955
                        $isCompressed = false;
2956
                    }
2957
2958
                    $pos += $len;
2959
                }
2960
            }
2961
2962
            // convert to UTF-8
2963
            $retstr = self::encodeUTF16($retstr, $isCompressed);
2964
2965
            // read additional Rich-Text information, if any
2966
            $fmtRuns = [];
2967
            if ($hasRichText) {
2968
                // list of formatting runs
2969
                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...
2970
                    // first formatted character; zero-based
2971
                    $charPos = self::getInt2d($recordData, $pos + $j * 4);
2972
2973
                    // index to font record
2974
                    $fontIndex = self::getInt2d($recordData, $pos + 2 + $j * 4);
2975
2976
                    $fmtRuns[] = [
2977
                        'charPos' => $charPos,
2978
                        'fontIndex' => $fontIndex,
2979
                    ];
2980
                }
2981
                $pos += 4 * $formattingRuns;
2982
            }
2983
2984
            // read additional Asian phonetics information, if any
2985
            if ($hasAsian) {
2986
                // For Asian phonetic settings, we skip the extended string data
2987
                $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...
2988
            }
2989
2990
            // store the shared sting
2991
            $this->sst[] = [
2992
                'value' => $retstr,
2993
                'fmtRuns' => $fmtRuns,
2994
            ];
2995
        }
2996
2997
        // getSplicedRecordData() takes care of moving current position in data stream
2998
    }
2999
3000
    /**
3001
     * Read PRINTGRIDLINES record
3002
     */
3003
    private function readPrintGridlines()
3004
    {
3005
        $length = self::getInt2d($this->data, $this->pos + 2);
3006
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3007
3008
        // move stream pointer to next record
3009
        $this->pos += 4 + $length;
3010
3011
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3012
            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
3013
            $printGridlines = (bool) self::getInt2d($recordData, 0);
3014
            $this->phpSheet->setPrintGridlines($printGridlines);
3015
        }
3016
    }
3017
3018
    /**
3019
     * Read DEFAULTROWHEIGHT record
3020
     */
3021 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...
3022
    {
3023
        $length = self::getInt2d($this->data, $this->pos + 2);
3024
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3025
3026
        // move stream pointer to next record
3027
        $this->pos += 4 + $length;
3028
3029
        // offset: 0; size: 2; option flags
3030
        // 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...
3031
        $height = self::getInt2d($recordData, 2);
3032
        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3033
    }
3034
3035
    /**
3036
     * Read SHEETPR record
3037
     */
3038
    private function readSheetPr()
3039
    {
3040
        $length = self::getInt2d($this->data, $this->pos + 2);
3041
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3042
3043
        // move stream pointer to next record
3044
        $this->pos += 4 + $length;
3045
3046
        // offset: 0; size: 2
3047
3048
        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
3049
        $isSummaryBelow = (0x0040 & self::getInt2d($recordData, 0)) >> 6;
3050
        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
3051
3052
        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
3053
        $isSummaryRight = (0x0080 & self::getInt2d($recordData, 0)) >> 7;
3054
        $this->phpSheet->setShowSummaryRight($isSummaryRight);
3055
3056
        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
3057
        // this corresponds to radio button setting in page setup dialog in Excel
3058
        $this->isFitToPages = (bool) ((0x0100 & self::getInt2d($recordData, 0)) >> 8);
3059
    }
3060
3061
    /**
3062
     * Read HORIZONTALPAGEBREAKS record
3063
     */
3064 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...
3065
    {
3066
        $length = self::getInt2d($this->data, $this->pos + 2);
3067
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3068
3069
        // move stream pointer to next record
3070
        $this->pos += 4 + $length;
3071
3072
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3073
            // offset: 0; size: 2; number of the following row index structures
3074
            $nm = self::getInt2d($recordData, 0);
3075
3076
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3077
            for ($i = 0; $i < $nm; ++$i) {
3078
                $r = self::getInt2d($recordData, 2 + 6 * $i);
3079
                $cf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3080
                $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...
3081
3082
                // not sure why two column indexes are necessary?
3083
                $this->phpSheet->setBreakByColumnAndRow($cf, $r, \PhpSpreadsheet\Worksheet::BREAK_ROW);
3084
            }
3085
        }
3086
    }
3087
3088
    /**
3089
     * Read VERTICALPAGEBREAKS record
3090
     */
3091 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...
3092
    {
3093
        $length = self::getInt2d($this->data, $this->pos + 2);
3094
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3095
3096
        // move stream pointer to next record
3097
        $this->pos += 4 + $length;
3098
3099
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3100
            // offset: 0; size: 2; number of the following column index structures
3101
            $nm = self::getInt2d($recordData, 0);
3102
3103
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3104
            for ($i = 0; $i < $nm; ++$i) {
3105
                $c = self::getInt2d($recordData, 2 + 6 * $i);
3106
                $rf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3107
                $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...
3108
3109
                // not sure why two row indexes are necessary?
3110
                $this->phpSheet->setBreakByColumnAndRow($c, $rf, \PhpSpreadsheet\Worksheet::BREAK_COLUMN);
3111
            }
3112
        }
3113
    }
3114
3115
    /**
3116
     * Read HEADER record
3117
     */
3118 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...
3119
    {
3120
        $length = self::getInt2d($this->data, $this->pos + 2);
3121
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3122
3123
        // move stream pointer to next record
3124
        $this->pos += 4 + $length;
3125
3126
        if (!$this->readDataOnly) {
3127
            // offset: 0; size: var
3128
            // realized that $recordData can be empty even when record exists
3129
            if ($recordData) {
3130
                if ($this->version == self::XLS_BIFF8) {
3131
                    $string = self::readUnicodeStringLong($recordData);
3132
                } else {
3133
                    $string = $this->readByteStringShort($recordData);
3134
                }
3135
3136
                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
3137
                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
3138
            }
3139
        }
3140
    }
3141
3142
    /**
3143
     * Read FOOTER record
3144
     */
3145 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...
3146
    {
3147
        $length = self::getInt2d($this->data, $this->pos + 2);
3148
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3149
3150
        // move stream pointer to next record
3151
        $this->pos += 4 + $length;
3152
3153
        if (!$this->readDataOnly) {
3154
            // offset: 0; size: var
3155
            // realized that $recordData can be empty even when record exists
3156
            if ($recordData) {
3157
                if ($this->version == self::XLS_BIFF8) {
3158
                    $string = self::readUnicodeStringLong($recordData);
3159
                } else {
3160
                    $string = $this->readByteStringShort($recordData);
3161
                }
3162
                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
3163
                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
3164
            }
3165
        }
3166
    }
3167
3168
    /**
3169
     * Read HCENTER record
3170
     */
3171 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...
3172
    {
3173
        $length = self::getInt2d($this->data, $this->pos + 2);
3174
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3175
3176
        // move stream pointer to next record
3177
        $this->pos += 4 + $length;
3178
3179
        if (!$this->readDataOnly) {
3180
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3181
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3182
3183
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3184
        }
3185
    }
3186
3187
    /**
3188
     * Read VCENTER record
3189
     */
3190 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...
3191
    {
3192
        $length = self::getInt2d($this->data, $this->pos + 2);
3193
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3194
3195
        // move stream pointer to next record
3196
        $this->pos += 4 + $length;
3197
3198
        if (!$this->readDataOnly) {
3199
            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
3200
            $isVerticalCentered = (bool) self::getInt2d($recordData, 0);
3201
3202
            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3203
        }
3204
    }
3205
3206
    /**
3207
     * Read LEFTMARGIN record
3208
     */
3209
    private function readLeftMargin()
3210
    {
3211
        $length = self::getInt2d($this->data, $this->pos + 2);
3212
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3213
3214
        // move stream pointer to next record
3215
        $this->pos += 4 + $length;
3216
3217
        if (!$this->readDataOnly) {
3218
            // offset: 0; size: 8
3219
            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3220
        }
3221
    }
3222
3223
    /**
3224
     * Read RIGHTMARGIN record
3225
     */
3226
    private function readRightMargin()
3227
    {
3228
        $length = self::getInt2d($this->data, $this->pos + 2);
3229
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3230
3231
        // move stream pointer to next record
3232
        $this->pos += 4 + $length;
3233
3234
        if (!$this->readDataOnly) {
3235
            // offset: 0; size: 8
3236
            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3237
        }
3238
    }
3239
3240
    /**
3241
     * Read TOPMARGIN record
3242
     */
3243
    private function readTopMargin()
3244
    {
3245
        $length = self::getInt2d($this->data, $this->pos + 2);
3246
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3247
3248
        // move stream pointer to next record
3249
        $this->pos += 4 + $length;
3250
3251
        if (!$this->readDataOnly) {
3252
            // offset: 0; size: 8
3253
            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3254
        }
3255
    }
3256
3257
    /**
3258
     * Read BOTTOMMARGIN record
3259
     */
3260
    private function readBottomMargin()
3261
    {
3262
        $length = self::getInt2d($this->data, $this->pos + 2);
3263
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3264
3265
        // move stream pointer to next record
3266
        $this->pos += 4 + $length;
3267
3268
        if (!$this->readDataOnly) {
3269
            // offset: 0; size: 8
3270
            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3271
        }
3272
    }
3273
3274
    /**
3275
     * Read PAGESETUP record
3276
     */
3277
    private function readPageSetup()
3278
    {
3279
        $length = self::getInt2d($this->data, $this->pos + 2);
3280
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3281
3282
        // move stream pointer to next record
3283
        $this->pos += 4 + $length;
3284
3285
        if (!$this->readDataOnly) {
3286
            // offset: 0; size: 2; paper size
3287
            $paperSize = self::getInt2d($recordData, 0);
3288
3289
            // offset: 2; size: 2; scaling factor
3290
            $scale = self::getInt2d($recordData, 2);
3291
3292
            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
3293
            $fitToWidth = self::getInt2d($recordData, 6);
3294
3295
            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
3296
            $fitToHeight = self::getInt2d($recordData, 8);
3297
3298
            // offset: 10; size: 2; option flags
3299
3300
            // 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...
3301
            $isPortrait = (0x0002 & self::getInt2d($recordData, 10)) >> 1;
3302
3303
            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
3304
            // when this bit is set, do not use flags for those properties
3305
            $isNotInit = (0x0004 & self::getInt2d($recordData, 10)) >> 2;
3306
3307
            if (!$isNotInit) {
3308
                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3309
                switch ($isPortrait) {
3310
                    case 0:
3311
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
3312
                        break;
3313
                    case 1:
3314
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
3315
                        break;
3316
                }
3317
3318
                $this->phpSheet->getPageSetup()->setScale($scale, false);
3319
                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
3320
                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
3321
                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
3322
            }
3323
3324
            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
3325
            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3326
            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3327
3328
            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
3329
            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3330
            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3331
        }
3332
    }
3333
3334
    /**
3335
     * PROTECT - Sheet protection (BIFF2 through BIFF8)
3336
     *   if this record is omitted, then it also means no sheet protection
3337
     */
3338 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...
3339
    {
3340
        $length = self::getInt2d($this->data, $this->pos + 2);
3341
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3342
3343
        // move stream pointer to next record
3344
        $this->pos += 4 + $length;
3345
3346
        if ($this->readDataOnly) {
3347
            return;
3348
        }
3349
3350
        // 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...
3351
3352
        // bit 0, mask 0x01; 1 = sheet is protected
3353
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3354
        $this->phpSheet->getProtection()->setSheet((bool) $bool);
3355
    }
3356
3357
    /**
3358
     * SCENPROTECT
3359
     */
3360 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...
3361
    {
3362
        $length = self::getInt2d($this->data, $this->pos + 2);
3363
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3364
3365
        // move stream pointer to next record
3366
        $this->pos += 4 + $length;
3367
3368
        if ($this->readDataOnly) {
3369
            return;
3370
        }
3371
3372
        // 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...
3373
3374
        // bit: 0, mask 0x01; 1 = scenarios are protected
3375
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3376
3377
        $this->phpSheet->getProtection()->setScenarios((bool) $bool);
3378
    }
3379
3380
    /**
3381
     * OBJECTPROTECT
3382
     */
3383 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...
3384
    {
3385
        $length = self::getInt2d($this->data, $this->pos + 2);
3386
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3387
3388
        // move stream pointer to next record
3389
        $this->pos += 4 + $length;
3390
3391
        if ($this->readDataOnly) {
3392
            return;
3393
        }
3394
3395
        // 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...
3396
3397
        // bit: 0, mask 0x01; 1 = objects are protected
3398
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3399
3400
        $this->phpSheet->getProtection()->setObjects((bool) $bool);
3401
    }
3402
3403
    /**
3404
     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
3405
     */
3406
    private function readPassword()
3407
    {
3408
        $length = self::getInt2d($this->data, $this->pos + 2);
3409
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3410
3411
        // move stream pointer to next record
3412
        $this->pos += 4 + $length;
3413
3414
        if (!$this->readDataOnly) {
3415
            // offset: 0; size: 2; 16-bit hash value of password
3416
            $password = strtoupper(dechex(self::getInt2d($recordData, 0))); // the hashed password
3417
            $this->phpSheet->getProtection()->setPassword($password, true);
3418
        }
3419
    }
3420
3421
    /**
3422
     * Read DEFCOLWIDTH record
3423
     */
3424 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...
3425
    {
3426
        $length = self::getInt2d($this->data, $this->pos + 2);
3427
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3428
3429
        // move stream pointer to next record
3430
        $this->pos += 4 + $length;
3431
3432
        // offset: 0; size: 2; default column width
3433
        $width = self::getInt2d($recordData, 0);
3434
        if ($width != 8) {
3435
            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3436
        }
3437
    }
3438
3439
    /**
3440
     * Read COLINFO record
3441
     */
3442
    private function readColInfo()
3443
    {
3444
        $length = self::getInt2d($this->data, $this->pos + 2);
3445
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3446
3447
        // move stream pointer to next record
3448
        $this->pos += 4 + $length;
3449
3450
        if (!$this->readDataOnly) {
3451
            // offset: 0; size: 2; index to first column in range
3452
            $fc = self::getInt2d($recordData, 0); // first column index
3453
3454
            // offset: 2; size: 2; index to last column in range
3455
            $lc = self::getInt2d($recordData, 2); // first column index
3456
3457
            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
3458
            $width = self::getInt2d($recordData, 4);
3459
3460
            // offset: 6; size: 2; index to XF record for default column formatting
3461
            $xfIndex = self::getInt2d($recordData, 6);
3462
3463
            // offset: 8; size: 2; option flags
3464
            // bit: 0; mask: 0x0001; 1= columns are hidden
3465
            $isHidden = (0x0001 & self::getInt2d($recordData, 8)) >> 0;
3466
3467
            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
3468
            $level = (0x0700 & self::getInt2d($recordData, 8)) >> 8;
3469
3470
            // bit: 12; mask: 0x1000; 1 = collapsed
3471
            $isCollapsed = (0x1000 & self::getInt2d($recordData, 8)) >> 12;
3472
3473
            // offset: 10; size: 2; not used
3474
3475
            for ($i = $fc; $i <= $lc; ++$i) {
3476
                if ($lc == 255 || $lc == 256) {
3477
                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3478
                    break;
3479
                }
3480
                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
3481
                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
3482
                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
3483
                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
3484
                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3485
            }
3486
        }
3487
    }
3488
3489
    /**
3490
     * ROW
3491
     *
3492
     * This record contains the properties of a single row in a
3493
     * sheet. Rows and cells in a sheet are divided into blocks
3494
     * of 32 rows.
3495
     *
3496
     * --    "OpenOffice.org's Documentation of the Microsoft
3497
     *         Excel File Format"
3498
     */
3499
    private function readRow()
3500
    {
3501
        $length = self::getInt2d($this->data, $this->pos + 2);
3502
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3503
3504
        // move stream pointer to next record
3505
        $this->pos += 4 + $length;
3506
3507
        if (!$this->readDataOnly) {
3508
            // offset: 0; size: 2; index of this row
3509
            $r = self::getInt2d($recordData, 0);
3510
3511
            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
3512
3513
            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
3514
3515
            // 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...
3516
3517
            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
3518
            $height = (0x7FFF & self::getInt2d($recordData, 6)) >> 0;
3519
3520
            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
3521
            $useDefaultHeight = (0x8000 & self::getInt2d($recordData, 6)) >> 15;
3522
3523
            if (!$useDefaultHeight) {
3524
                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
3525
            }
3526
3527
            // offset: 8; size: 2; not used
3528
3529
            // offset: 10; size: 2; not used in BIFF5-BIFF8
3530
3531
            // offset: 12; size: 4; option flags and default row formatting
3532
3533
            // bit: 2-0: mask: 0x00000007; outline level of the row
3534
            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3535
            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
3536
3537
            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
3538
            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
3539
            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
3540
3541
            // bit: 5; mask: 0x00000020; 1 = row is hidden
3542
            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3543
            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
3544
3545
            // bit: 7; mask: 0x00000080; 1 = row has explicit format
3546
            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3547
3548
            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
3549
            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3550
3551
            if ($hasExplicitFormat) {
3552
                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3553
            }
3554
        }
3555
    }
3556
3557
    /**
3558
     * Read RK record
3559
     * This record represents a cell that contains an RK value
3560
     * (encoded integer or floating-point value). If a
3561
     * floating-point value cannot be encoded to an RK value,
3562
     * a NUMBER record will be written. This record replaces the
3563
     * record INTEGER written in BIFF2.
3564
     *
3565
     * --    "OpenOffice.org's Documentation of the Microsoft
3566
     *         Excel File Format"
3567
     */
3568 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...
3569
    {
3570
        $length = self::getInt2d($this->data, $this->pos + 2);
3571
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3572
3573
        // move stream pointer to next record
3574
        $this->pos += 4 + $length;
3575
3576
        // offset: 0; size: 2; index to row
3577
        $row = self::getInt2d($recordData, 0);
3578
3579
        // offset: 2; size: 2; index to column
3580
        $column = self::getInt2d($recordData, 2);
3581
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3582
3583
        // Read cell?
3584
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3585
            // offset: 4; size: 2; index to XF record
3586
            $xfIndex = self::getInt2d($recordData, 4);
3587
3588
            // offset: 6; size: 4; RK value
3589
            $rknum = self::getInt4d($recordData, 6);
3590
            $numValue = self::getIEEE754($rknum);
3591
3592
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3593
            if (!$this->readDataOnly) {
3594
                // add style information
3595
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3596
            }
3597
3598
            // add cell
3599
            $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3600
        }
3601
    }
3602
3603
    /**
3604
     * Read LABELSST record
3605
     * This record represents a cell that contains a string. It
3606
     * replaces the LABEL record and RSTRING record used in
3607
     * BIFF2-BIFF5.
3608
     *
3609
     * --    "OpenOffice.org's Documentation of the Microsoft
3610
     *         Excel File Format"
3611
     */
3612
    private function readLabelSst()
3613
    {
3614
        $length = self::getInt2d($this->data, $this->pos + 2);
3615
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3616
3617
        // move stream pointer to next record
3618
        $this->pos += 4 + $length;
3619
3620
        // offset: 0; size: 2; index to row
3621
        $row = self::getInt2d($recordData, 0);
3622
3623
        // offset: 2; size: 2; index to column
3624
        $column = self::getInt2d($recordData, 2);
3625
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3626
3627
        $emptyCell = true;
3628
        // Read cell?
3629
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3630
            // offset: 4; size: 2; index to XF record
3631
            $xfIndex = self::getInt2d($recordData, 4);
3632
3633
            // offset: 6; size: 4; index to SST record
3634
            $index = self::getInt4d($recordData, 6);
3635
3636
            // add cell
3637
            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
3638
                // then we should treat as rich text
3639
                $richText = new \PhpSpreadsheet\RichText();
3640
                $charPos = 0;
3641
                $sstCount = count($this->sst[$index]['fmtRuns']);
3642
                for ($i = 0; $i <= $sstCount; ++$i) {
3643
                    if (isset($fmtRuns[$i])) {
3644
                        $text = \PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
3645
                        $charPos = $fmtRuns[$i]['charPos'];
3646
                    } else {
3647
                        $text = \PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, \PhpSpreadsheet\Shared\StringHelper::countCharacters($this->sst[$index]['value']));
3648
                    }
3649
3650
                    if (\PhpSpreadsheet\Shared\StringHelper::countCharacters($text) > 0) {
3651
                        if ($i == 0) { // first text run, no style
3652
                            $richText->createText($text);
3653
                        } else {
3654
                            $textRun = $richText->createTextRun($text);
3655
                            if (isset($fmtRuns[$i - 1])) {
3656
                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
3657
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
3658
                                } else {
3659
                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
3660
                                    // check the OpenOffice documentation of the FONT record
3661
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
3662
                                }
3663
                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
3664
                            }
3665
                        }
3666
                    }
3667
                }
3668
                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
3669
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3670
                    $cell->setValueExplicit($richText, \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3671
                    $emptyCell = false;
3672
                }
3673
            } else {
3674
                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
3675
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3676
                    $cell->setValueExplicit($this->sst[$index]['value'], \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3677
                    $emptyCell = false;
3678
                }
3679
            }
3680
3681
            if (!$this->readDataOnly && !$emptyCell) {
3682
                // add style information
3683
                $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...
3684
            }
3685
        }
3686
    }
3687
3688
    /**
3689
     * Read MULRK record
3690
     * This record represents a cell range containing RK value
3691
     * cells. All cells are located in the same row.
3692
     *
3693
     * --    "OpenOffice.org's Documentation of the Microsoft
3694
     *         Excel File Format"
3695
     */
3696
    private function readMulRk()
3697
    {
3698
        $length = self::getInt2d($this->data, $this->pos + 2);
3699
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3700
3701
        // move stream pointer to next record
3702
        $this->pos += 4 + $length;
3703
3704
        // offset: 0; size: 2; index to row
3705
        $row = self::getInt2d($recordData, 0);
3706
3707
        // offset: 2; size: 2; index to first column
3708
        $colFirst = self::getInt2d($recordData, 2);
3709
3710
        // offset: var; size: 2; index to last column
3711
        $colLast = self::getInt2d($recordData, $length - 2);
3712
        $columns = $colLast - $colFirst + 1;
3713
3714
        // offset within record data
3715
        $offset = 4;
3716
3717
        for ($i = 0; $i < $columns; ++$i) {
3718
            $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($colFirst + $i);
3719
3720
            // Read cell?
3721
            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3722
                // offset: var; size: 2; index to XF record
3723
                $xfIndex = self::getInt2d($recordData, $offset);
3724
3725
                // offset: var; size: 4; RK value
3726
                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3727
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3728
                if (!$this->readDataOnly) {
3729
                    // add style
3730
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3731
                }
3732
3733
                // add cell value
3734
                $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3735
            }
3736
3737
            $offset += 6;
3738
        }
3739
    }
3740
3741
    /**
3742
     * Read NUMBER record
3743
     * This record represents a cell that contains a
3744
     * floating-point value.
3745
     *
3746
     * --    "OpenOffice.org's Documentation of the Microsoft
3747
     *         Excel File Format"
3748
     */
3749 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...
3750
    {
3751
        $length = self::getInt2d($this->data, $this->pos + 2);
3752
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3753
3754
        // move stream pointer to next record
3755
        $this->pos += 4 + $length;
3756
3757
        // offset: 0; size: 2; index to row
3758
        $row = self::getInt2d($recordData, 0);
3759
3760
        // offset: 2; size 2; index to column
3761
        $column = self::getInt2d($recordData, 2);
3762
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3763
3764
        // Read cell?
3765
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3766
            // offset 4; size: 2; index to XF record
3767
            $xfIndex = self::getInt2d($recordData, 4);
3768
3769
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3770
3771
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3772
            if (!$this->readDataOnly) {
3773
                // add cell style
3774
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3775
            }
3776
3777
            // add cell value
3778
            $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3779
        }
3780
    }
3781
3782
    /**
3783
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3784
     * This record contains the token array and the result of a
3785
     * formula cell.
3786
     *
3787
     * --    "OpenOffice.org's Documentation of the Microsoft
3788
     *         Excel File Format"
3789
     */
3790
    private function readFormula()
3791
    {
3792
        $length = self::getInt2d($this->data, $this->pos + 2);
3793
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3794
3795
        // move stream pointer to next record
3796
        $this->pos += 4 + $length;
3797
3798
        // offset: 0; size: 2; row index
3799
        $row = self::getInt2d($recordData, 0);
3800
3801
        // offset: 2; size: 2; col index
3802
        $column = self::getInt2d($recordData, 2);
3803
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3804
3805
        // offset: 20: size: variable; formula structure
3806
        $formulaStructure = substr($recordData, 20);
3807
3808
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3809
        $options = self::getInt2d($recordData, 14);
3810
3811
        // bit: 0; mask: 0x0001; 1 = recalculate always
3812
        // bit: 1; mask: 0x0002; 1 = calculate on open
3813
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3814
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3815
3816
        // WARNING:
3817
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3818
        // the formula data may be ordinary formula data, therefore we need to check
3819
        // explicitly for the tExp token (0x01)
3820
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01;
3821
3822
        if ($isPartOfSharedFormula) {
3823
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3824
            // get the base cell, grab tExp token
3825
            $baseRow = self::getInt2d($formulaStructure, 3);
3826
            $baseCol = self::getInt2d($formulaStructure, 5);
3827
            $this->_baseCell = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol) . ($baseRow + 1);
0 ignored issues
show
Bug introduced by
The property _baseCell does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
3828
        }
3829
3830
        // Read cell?
3831
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3832
            if ($isPartOfSharedFormula) {
3833
                // formula is added to this cell after the sheet has been read
3834
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3835
            }
3836
3837
            // offset: 16: size: 4; not used
3838
3839
            // offset: 4; size: 2; XF index
3840
            $xfIndex = self::getInt2d($recordData, 4);
3841
3842
            // offset: 6; size: 8; result of the formula
3843
            if ((ord($recordData{6}) == 0) && (ord($recordData{12}) == 255) && (ord($recordData{13}) == 255)) {
3844
                // String formula. Result follows in appended STRING record
3845
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_STRING;
3846
3847
                // read possible SHAREDFMLA record
3848
                $code = self::getInt2d($this->data, $this->pos);
3849
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3850
                    $this->readSharedFmla();
3851
                }
3852
3853
                // read STRING record
3854
                $value = $this->readString();
3855 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...
3856
                && (ord($recordData{12}) == 255)
3857
                && (ord($recordData{13}) == 255)) {
3858
                // Boolean formula. Result is in +2; 0=false, 1=true
3859
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
3860
                $value = (bool) ord($recordData{8});
3861
            } elseif ((ord($recordData{6}) == 2)
3862
                && (ord($recordData{12}) == 255)
3863
                && (ord($recordData{13}) == 255)) {
3864
                // Error formula. Error code is in +2
3865
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
3866
                $value = Excel5\ErrorCode::lookup(ord($recordData{8}));
3867 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...
3868
                && (ord($recordData{12}) == 255)
3869
                && (ord($recordData{13}) == 255)) {
3870
                // Formula result is a null string
3871
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_NULL;
3872
                $value = '';
3873
            } else {
3874
                // forumla result is a number, first 14 bytes like _NUMBER record
3875
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
3876
                $value = self::extractNumber(substr($recordData, 6, 8));
3877
            }
3878
3879
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3880
            if (!$this->readDataOnly) {
3881
                // add cell style
3882
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3883
            }
3884
3885
            // store the formula
3886
            if (!$isPartOfSharedFormula) {
3887
                // not part of shared formula
3888
                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
3889
                try {
3890
                    if ($this->version != self::XLS_BIFF8) {
3891
                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
3892
                    }
3893
                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
3894
                    $cell->setValueExplicit('=' . $formula, \PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
3895
                } catch (\PhpSpreadsheet\Exception $e) {
3896
                    $cell->setValueExplicit($value, $dataType);
3897
                }
3898
            } else {
3899
                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...
3900
                    // do nothing at this point, formula id added later in the code
3901
                } else {
3902
                    $cell->setValueExplicit($value, $dataType);
3903
                }
3904
            }
3905
3906
            // store the cached calculated value
3907
            $cell->setCalculatedValue($value);
3908
        }
3909
    }
3910
3911
    /**
3912
     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
3913
     * which usually contains relative references.
3914
     * These will be used to construct the formula in each shared formula part after the sheet is read.
3915
     */
3916
    private function readSharedFmla()
3917
    {
3918
        $length = self::getInt2d($this->data, $this->pos + 2);
3919
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3920
3921
        // move stream pointer to next record
3922
        $this->pos += 4 + $length;
3923
3924
        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
3925
        $cellRange = substr($recordData, 0, 6);
3926
        $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...
3927
3928
        // offset: 6, size: 1; not used
3929
3930
        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
3931
        $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...
3932
3933
        // offset: 8, size: var; Binary token array of the shared formula
3934
        $formula = substr($recordData, 8);
3935
3936
        // at this point we only store the shared formula for later use
3937
        $this->sharedFormulas[$this->_baseCell] = $formula;
3938
    }
3939
3940
    /**
3941
     * Read a STRING record from current stream position and advance the stream pointer to next record
3942
     * This record is used for storing result from FORMULA record when it is a string, and
3943
     * it occurs directly after the FORMULA record
3944
     *
3945
     * @return string The string contents as UTF-8
3946
     */
3947
    private function readString()
3948
    {
3949
        $length = self::getInt2d($this->data, $this->pos + 2);
3950
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3951
3952
        // move stream pointer to next record
3953
        $this->pos += 4 + $length;
3954
3955
        if ($this->version == self::XLS_BIFF8) {
3956
            $string = self::readUnicodeStringLong($recordData);
3957
            $value = $string['value'];
3958
        } else {
3959
            $string = $this->readByteStringLong($recordData);
3960
            $value = $string['value'];
3961
        }
3962
3963
        return $value;
3964
    }
3965
3966
    /**
3967
     * Read BOOLERR record
3968
     * This record represents a Boolean value or error value
3969
     * cell.
3970
     *
3971
     * --    "OpenOffice.org's Documentation of the Microsoft
3972
     *         Excel File Format"
3973
     */
3974
    private function readBoolErr()
3975
    {
3976
        $length = self::getInt2d($this->data, $this->pos + 2);
3977
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3978
3979
        // move stream pointer to next record
3980
        $this->pos += 4 + $length;
3981
3982
        // offset: 0; size: 2; row index
3983
        $row = self::getInt2d($recordData, 0);
3984
3985
        // offset: 2; size: 2; column index
3986
        $column = self::getInt2d($recordData, 2);
3987
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3988
3989
        // Read cell?
3990
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3991
            // offset: 4; size: 2; index to XF record
3992
            $xfIndex = self::getInt2d($recordData, 4);
3993
3994
            // offset: 6; size: 1; the boolean value or error value
3995
            $boolErr = ord($recordData{6});
3996
3997
            // 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...
3998
            $isError = ord($recordData{7});
3999
4000
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4001
            switch ($isError) {
4002
                case 0: // boolean
4003
                    $value = (bool) $boolErr;
4004
4005
                    // add cell value
4006
                    $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_BOOL);
4007
                    break;
4008
                case 1: // error type
4009
                    $value = Excel5\ErrorCode::lookup($boolErr);
4010
4011
                    // add cell value
4012
                    $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_ERROR);
4013
                    break;
4014
            }
4015
4016
            if (!$this->readDataOnly) {
4017
                // add cell style
4018
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4019
            }
4020
        }
4021
    }
4022
4023
    /**
4024
     * Read MULBLANK record
4025
     * This record represents a cell range of empty cells. All
4026
     * cells are located in the same row
4027
     *
4028
     * --    "OpenOffice.org's Documentation of the Microsoft
4029
     *         Excel File Format"
4030
     */
4031
    private function readMulBlank()
4032
    {
4033
        $length = self::getInt2d($this->data, $this->pos + 2);
4034
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4035
4036
        // move stream pointer to next record
4037
        $this->pos += 4 + $length;
4038
4039
        // offset: 0; size: 2; index to row
4040
        $row = self::getInt2d($recordData, 0);
4041
4042
        // offset: 2; size: 2; index to first column
4043
        $fc = self::getInt2d($recordData, 2);
4044
4045
        // offset: 4; size: 2 x nc; list of indexes to XF records
4046
        // add style information
4047
        if (!$this->readDataOnly && $this->readEmptyCells) {
4048
            for ($i = 0; $i < $length / 2 - 3; ++$i) {
4049
                $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc + $i);
4050
4051
                // Read cell?
4052
                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4053
                    $xfIndex = self::getInt2d($recordData, 4 + 2 * $i);
4054
                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4055
                }
4056
            }
4057
        }
4058
4059
        // offset: 6; size 2; index to last column (not needed)
4060
    }
4061
4062
    /**
4063
     * Read LABEL record
4064
     * This record represents a cell that contains a string. In
4065
     * BIFF8 it is usually replaced by the LABELSST record.
4066
     * Excel still uses this record, if it copies unformatted
4067
     * text cells to the clipboard.
4068
     *
4069
     * --    "OpenOffice.org's Documentation of the Microsoft
4070
     *         Excel File Format"
4071
     */
4072
    private function readLabel()
4073
    {
4074
        $length = self::getInt2d($this->data, $this->pos + 2);
4075
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4076
4077
        // move stream pointer to next record
4078
        $this->pos += 4 + $length;
4079
4080
        // offset: 0; size: 2; index to row
4081
        $row = self::getInt2d($recordData, 0);
4082
4083
        // offset: 2; size: 2; index to column
4084
        $column = self::getInt2d($recordData, 2);
4085
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
4086
4087
        // Read cell?
4088
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4089
            // offset: 4; size: 2; XF index
4090
            $xfIndex = self::getInt2d($recordData, 4);
4091
4092
            // add cell value
4093
            // todo: what if string is very long? continue record
4094 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...
4095
                $string = self::readUnicodeStringLong(substr($recordData, 6));
4096
                $value = $string['value'];
4097
            } else {
4098
                $string = $this->readByteStringLong(substr($recordData, 6));
4099
                $value = $string['value'];
4100
            }
4101
            if ($this->readEmptyCells || trim($value) !== '') {
4102
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4103
                $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
4104
4105
                if (!$this->readDataOnly) {
4106
                    // add cell style
4107
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4108
                }
4109
            }
4110
        }
4111
    }
4112
4113
    /**
4114
     * Read BLANK record
4115
     */
4116
    private function readBlank()
4117
    {
4118
        $length = self::getInt2d($this->data, $this->pos + 2);
4119
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4120
4121
        // move stream pointer to next record
4122
        $this->pos += 4 + $length;
4123
4124
        // offset: 0; size: 2; row index
4125
        $row = self::getInt2d($recordData, 0);
4126
4127
        // offset: 2; size: 2; col index
4128
        $col = self::getInt2d($recordData, 2);
4129
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($col);
4130
4131
        // Read cell?
4132
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4133
            // offset: 4; size: 2; XF index
4134
            $xfIndex = self::getInt2d($recordData, 4);
4135
4136
            // add style information
4137
            if (!$this->readDataOnly && $this->readEmptyCells) {
4138
                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4139
            }
4140
        }
4141
    }
4142
4143
    /**
4144
     * Read MSODRAWING record
4145
     */
4146 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...
4147
    {
4148
        $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...
4149
4150
        // get spliced record data
4151
        $splicedRecordData = $this->getSplicedRecordData();
4152
        $recordData = $splicedRecordData['recordData'];
4153
4154
        $this->drawingData .= $recordData;
4155
    }
4156
4157
    /**
4158
     * Read OBJ record
4159
     */
4160
    private function readObj()
4161
    {
4162
        $length = self::getInt2d($this->data, $this->pos + 2);
4163
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4164
4165
        // move stream pointer to next record
4166
        $this->pos += 4 + $length;
4167
4168
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4169
            return;
4170
        }
4171
4172
        // recordData consists of an array of subrecords looking like this:
4173
        //    ft: 2 bytes; ftCmo type (0x15)
4174
        //    cb: 2 bytes; size in bytes of ftCmo data
4175
        //    ot: 2 bytes; Object Type
4176
        //    id: 2 bytes; Object id number
4177
        //    grbit: 2 bytes; Option Flags
4178
        //    data: var; subrecord data
4179
4180
        // for now, we are just interested in the second subrecord containing the object type
4181
        $ftCmoType = self::getInt2d($recordData, 0);
4182
        $cbCmoSize = self::getInt2d($recordData, 2);
4183
        $otObjType = self::getInt2d($recordData, 4);
4184
        $idObjID = self::getInt2d($recordData, 6);
4185
        $grbitOpts = self::getInt2d($recordData, 6);
4186
4187
        $this->objs[] = [
4188
            'ftCmoType' => $ftCmoType,
4189
            'cbCmoSize' => $cbCmoSize,
4190
            'otObjType' => $otObjType,
4191
            'idObjID' => $idObjID,
4192
            'grbitOpts' => $grbitOpts,
4193
        ];
4194
        $this->textObjRef = $idObjID;
4195
4196
//        echo '<b>_readObj()</b><br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
4197
//        var_dump(end($this->objs));
4198
//        echo '<br />';
4199
    }
4200
4201
    /**
4202
     * Read WINDOW2 record
4203
     */
4204
    private function readWindow2()
4205
    {
4206
        $length = self::getInt2d($this->data, $this->pos + 2);
4207
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4208
4209
        // move stream pointer to next record
4210
        $this->pos += 4 + $length;
4211
4212
        // offset: 0; size: 2; option flags
4213
        $options = self::getInt2d($recordData, 0);
4214
4215
        // offset: 2; size: 2; index to first visible row
4216
        $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...
4217
4218
        // offset: 4; size: 2; index to first visible colum
4219
        $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...
4220
        if ($this->version === self::XLS_BIFF8) {
4221
            // offset:  8; size: 2; not used
4222
            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
4223
            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
4224
            // offset: 14; size: 4; not used
4225
            $zoomscaleInPageBreakPreview = self::getInt2d($recordData, 10);
4226
            if ($zoomscaleInPageBreakPreview === 0) {
4227
                $zoomscaleInPageBreakPreview = 60;
4228
            }
4229
            $zoomscaleInNormalView = self::getInt2d($recordData, 12);
4230
            if ($zoomscaleInNormalView === 0) {
4231
                $zoomscaleInNormalView = 100;
4232
            }
4233
        }
4234
4235
        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
4236
        $showGridlines = (bool) ((0x0002 & $options) >> 1);
4237
        $this->phpSheet->setShowGridlines($showGridlines);
4238
4239
        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
4240
        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4241
        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4242
4243
        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
4244
        $this->frozen = (bool) ((0x0008 & $options) >> 3);
4245
4246
        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
4247
        $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
4248
4249
        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
4250
        $isActive = (bool) ((0x0400 & $options) >> 10);
4251
        if ($isActive) {
4252
            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4253
        }
4254
4255
        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
4256
        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4257
4258
        //FIXME: set $firstVisibleRow and $firstVisibleColumn
4259
4260
        if ($this->phpSheet->getSheetView()->getView() !== \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT) {
4261
            //NOTE: this setting is inferior to page layout view(Excel2007-)
4262
            $view = $isPageBreakPreview ? \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_NORMAL;
4263
            $this->phpSheet->getSheetView()->setView($view);
4264
            if ($this->version === self::XLS_BIFF8) {
4265
                $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...
4266
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4267
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4268
            }
4269
        }
4270
    }
4271
4272
    /**
4273
     * Read PLV Record(Created by Excel2007 or upper)
4274
     */
4275
    private function readPageLayoutView()
4276
    {
4277
        $length = self::getInt2d($this->data, $this->pos + 2);
4278
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4279
4280
        // move stream pointer to next record
4281
        $this->pos += 4 + $length;
4282
4283
        //var_dump(unpack("vrt/vgrbitFrt/V2reserved/vwScalePLV/vgrbit", $recordData));
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% 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...
4284
4285
        // offset: 0; size: 2; rt
4286
        //->ignore
4287
        $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...
4288
        // offset: 2; size: 2; grbitfr
4289
        //->ignore
4290
        $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...
4291
        // offset: 4; size: 8; reserved
4292
        //->ignore
4293
4294
        // offset: 12; size 2; zoom scale
4295
        $wScalePLV = self::getInt2d($recordData, 12);
4296
        // offset: 14; size 2; grbit
4297
        $grbit = self::getInt2d($recordData, 14);
4298
4299
        // decomprise grbit
4300
        $fPageLayoutView = $grbit & 0x01;
4301
        $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...
4302
        $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...
4303
4304
        if ($fPageLayoutView === 1) {
4305
            $this->phpSheet->getSheetView()->setView(\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT);
4306
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4307
        }
4308
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4309
    }
4310
4311
    /**
4312
     * Read SCL record
4313
     */
4314
    private function readScl()
4315
    {
4316
        $length = self::getInt2d($this->data, $this->pos + 2);
4317
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4318
4319
        // move stream pointer to next record
4320
        $this->pos += 4 + $length;
4321
4322
        // offset: 0; size: 2; numerator of the view magnification
4323
        $numerator = self::getInt2d($recordData, 0);
4324
4325
        // offset: 2; size: 2; numerator of the view magnification
4326
        $denumerator = self::getInt2d($recordData, 2);
4327
4328
        // set the zoom scale (in percent)
4329
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4330
    }
4331
4332
    /**
4333
     * Read PANE record
4334
     */
4335
    private function readPane()
4336
    {
4337
        $length = self::getInt2d($this->data, $this->pos + 2);
4338
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4339
4340
        // move stream pointer to next record
4341
        $this->pos += 4 + $length;
4342
4343
        if (!$this->readDataOnly) {
4344
            // offset: 0; size: 2; position of vertical split
4345
            $px = self::getInt2d($recordData, 0);
4346
4347
            // offset: 2; size: 2; position of horizontal split
4348
            $py = self::getInt2d($recordData, 2);
4349
4350
            if ($this->frozen) {
4351
                // frozen panes
4352
                $this->phpSheet->freezePane(\PhpSpreadsheet\Cell::stringFromColumnIndex($px) . ($py + 1));
4353
            } 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...
4354
                // unfrozen panes; split windows; not supported by PhpSpreadsheet core
4355
            }
4356
        }
4357
    }
4358
4359
    /**
4360
     * Read SELECTION record. There is one such record for each pane in the sheet.
4361
     */
4362
    private function readSelection()
4363
    {
4364
        $length = self::getInt2d($this->data, $this->pos + 2);
4365
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4366
4367
        // move stream pointer to next record
4368
        $this->pos += 4 + $length;
4369
4370
        if (!$this->readDataOnly) {
4371
            // offset: 0; size: 1; pane identifier
4372
            $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...
4373
4374
            // offset: 1; size: 2; index to row of the active cell
4375
            $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...
4376
4377
            // offset: 3; size: 2; index to column of the active cell
4378
            $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...
4379
4380
            // offset: 5; size: 2; index into the following cell range list to the
4381
            //  entry that contains the active cell
4382
            $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...
4383
4384
            // offset: 7; size: var; cell range address list containing all selected cell ranges
4385
            $data = substr($recordData, 7);
4386
            $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
4387
4388
            $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
4389
4390
            // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
4391
            if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
4392
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
4393
            }
4394
4395
            // first row '1' + last row '65536' indicates that full column is selected
4396
            if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
4397
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
4398
            }
4399
4400
            // first column 'A' + last column 'IV' indicates that full row is selected
4401
            if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
4402
                $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
4403
            }
4404
4405
            $this->phpSheet->setSelectedCells($selectedCells);
4406
        }
4407
    }
4408
4409
    private function includeCellRangeFiltered($cellRangeAddress)
4410
    {
4411
        $includeCellRange = true;
4412
        if ($this->getReadFilter() !== null) {
4413
            $includeCellRange = false;
4414
            $rangeBoundaries = \PhpSpreadsheet\Cell::getRangeBoundaries($cellRangeAddress);
4415
            ++$rangeBoundaries[1][0];
4416
            for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
4417
                for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
4418
                    if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
4419
                        $includeCellRange = true;
4420
                        break 2;
4421
                    }
4422
                }
4423
            }
4424
        }
4425
4426
        return $includeCellRange;
4427
    }
4428
4429
    /**
4430
     * MERGEDCELLS
4431
     *
4432
     * This record contains the addresses of merged cell ranges
4433
     * in the current sheet.
4434
     *
4435
     * --    "OpenOffice.org's Documentation of the Microsoft
4436
     *         Excel File Format"
4437
     */
4438
    private function readMergedCells()
4439
    {
4440
        $length = self::getInt2d($this->data, $this->pos + 2);
4441
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4442
4443
        // move stream pointer to next record
4444
        $this->pos += 4 + $length;
4445
4446
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
4447
            $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
4448
            foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
4449
                if ((strpos($cellRangeAddress, ':') !== false) &&
4450
                    ($this->includeCellRangeFiltered($cellRangeAddress))) {
4451
                    $this->phpSheet->mergeCells($cellRangeAddress);
4452
                }
4453
            }
4454
        }
4455
    }
4456
4457
    /**
4458
     * Read HYPERLINK record
4459
     */
4460
    private function readHyperLink()
4461
    {
4462
        $length = self::getInt2d($this->data, $this->pos + 2);
4463
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4464
4465
        // move stream pointer forward to next record
4466
        $this->pos += 4 + $length;
4467
4468
        if (!$this->readDataOnly) {
4469
            // offset: 0; size: 8; cell range address of all cells containing this hyperlink
4470
            try {
4471
                $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData, 0, 8);
0 ignored issues
show
Unused Code introduced by
The call to Excel5::readBIFF8CellRangeAddressFixed() has too many arguments starting with 0.

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

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

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

Loading history...
4472
            } catch (\PhpSpreadsheet\Exception $e) {
4473
                return;
4474
            }
4475
4476
            // offset: 8, size: 16; GUID of StdLink
4477
4478
            // offset: 24, size: 4; unknown value
4479
4480
            // offset: 28, size: 4; option flags
4481
            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
4482
            $isFileLinkOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 0;
4483
4484
            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
4485
            $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...
4486
4487
            // 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...
4488
            $hasDesc = (0x00000014 & self::getInt2d($recordData, 28)) >> 2;
4489
4490
            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
4491
            $hasText = (0x00000008 & self::getInt2d($recordData, 28)) >> 3;
4492
4493
            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
4494
            $hasFrame = (0x00000080 & self::getInt2d($recordData, 28)) >> 7;
4495
4496
            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
4497
            $isUNC = (0x00000100 & self::getInt2d($recordData, 28)) >> 8;
4498
4499
            // offset within record data
4500
            $offset = 32;
4501
4502
            if ($hasDesc) {
4503
                // offset: 32; size: var; character count of description text
4504
                $dl = self::getInt4d($recordData, 32);
4505
                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
4506
                $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...
4507
                $offset += 4 + 2 * $dl;
4508
            }
4509
            if ($hasFrame) {
4510
                $fl = self::getInt4d($recordData, $offset);
4511
                $offset += 4 + 2 * $fl;
4512
            }
4513
4514
            // detect type of hyperlink (there are 4 types)
4515
            $hyperlinkType = null;
4516
4517
            if ($isUNC) {
4518
                $hyperlinkType = 'UNC';
4519
            } elseif (!$isFileLinkOrUrl) {
4520
                $hyperlinkType = 'workbook';
4521
            } elseif (ord($recordData{$offset}) == 0x03) {
4522
                $hyperlinkType = 'local';
4523
            } elseif (ord($recordData{$offset}) == 0xE0) {
4524
                $hyperlinkType = 'URL';
4525
            }
4526
4527
            switch ($hyperlinkType) {
4528
                case 'URL':
4529
                    // section 5.58.2: Hyperlink containing a URL
4530
                    // e.g. http://example.org/index.php
4531
4532
                    // offset: var; size: 16; GUID of URL Moniker
4533
                    $offset += 16;
4534
                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
4535
                    $us = self::getInt4d($recordData, $offset);
4536
                    $offset += 4;
4537
                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
4538
                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
4539
                    $nullOffset = strpos($url, 0x00);
4540
                    if ($nullOffset) {
4541
                        $url = substr($url, 0, $nullOffset);
4542
                    }
4543
                    $url .= $hasText ? '#' : '';
4544
                    $offset += $us;
4545
                    break;
4546
                case 'local':
4547
                    // section 5.58.3: Hyperlink to local file
4548
                    // examples:
4549
                    //   mydoc.txt
4550
                    //   ../../somedoc.xls#Sheet!A1
4551
4552
                    // offset: var; size: 16; GUI of File Moniker
4553
                    $offset += 16;
4554
4555
                    // offset: var; size: 2; directory up-level count.
4556
                    $upLevelCount = self::getInt2d($recordData, $offset);
4557
                    $offset += 2;
4558
4559
                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
4560
                    $sl = self::getInt4d($recordData, $offset);
4561
                    $offset += 4;
4562
4563
                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
4564
                    $shortenedFilePath = substr($recordData, $offset, $sl);
4565
                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
4566
                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
4567
4568
                    $offset += $sl;
4569
4570
                    // offset: var; size: 24; unknown sequence
4571
                    $offset += 24;
4572
4573
                    // extended file path
4574
                    // offset: var; size: 4; size of the following file link field including string lenth mark
4575
                    $sz = self::getInt4d($recordData, $offset);
4576
                    $offset += 4;
4577
4578
                    // only present if $sz > 0
4579
                    if ($sz > 0) {
4580
                        // offset: var; size: 4; size of the character array of the extended file path and name
4581
                        $xl = self::getInt4d($recordData, $offset);
4582
                        $offset += 4;
4583
4584
                        // offset: var; size 2; unknown
4585
                        $offset += 2;
4586
4587
                        // offset: var; size $xl; character array of the extended file path and name.
4588
                        $extendedFilePath = substr($recordData, $offset, $xl);
4589
                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
4590
                        $offset += $xl;
4591
                    }
4592
4593
                    // construct the path
4594
                    $url = str_repeat('..\\', $upLevelCount);
4595
                    $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...
4596
                    $url .= $hasText ? '#' : '';
4597
4598
                    break;
4599
                case 'UNC':
4600
                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
4601
                    // todo: implement
4602
                    return;
4603
                case 'workbook':
4604
                    // section 5.58.5: Hyperlink to the Current Workbook
4605
                    // e.g. Sheet2!B1:C2, stored in text mark field
4606
                    $url = 'sheet://';
4607
                    break;
4608
                default:
4609
                    return;
4610
            }
4611
4612
            if ($hasText) {
4613
                // offset: var; size: 4; character count of text mark including trailing zero word
4614
                $tl = self::getInt4d($recordData, $offset);
4615
                $offset += 4;
4616
                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
4617
                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
4618
                $url .= $text;
4619
            }
4620
4621
            // apply the hyperlink to all the relevant cells
4622
            foreach (\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
4623
                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
4624
            }
4625
        }
4626
    }
4627
4628
    /**
4629
     * Read DATAVALIDATIONS record
4630
     */
4631
    private function readDataValidations()
4632
    {
4633
        $length = self::getInt2d($this->data, $this->pos + 2);
4634
        $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...
4635
4636
        // move stream pointer forward to next record
4637
        $this->pos += 4 + $length;
4638
    }
4639
4640
    /**
4641
     * Read DATAVALIDATION record
4642
     */
4643
    private function readDataValidation()
4644
    {
4645
        $length = self::getInt2d($this->data, $this->pos + 2);
4646
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4647
4648
        // move stream pointer forward to next record
4649
        $this->pos += 4 + $length;
4650
4651
        if ($this->readDataOnly) {
4652
            return;
4653
        }
4654
4655
        // offset: 0; size: 4; Options
4656
        $options = self::getInt4d($recordData, 0);
4657
4658
        // 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...
4659
        $type = (0x0000000F & $options) >> 0;
4660 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...
4661
            case 0x00:
4662
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_NONE;
4663
                break;
4664
            case 0x01:
4665
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE;
4666
                break;
4667
            case 0x02:
4668
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_DECIMAL;
4669
                break;
4670
            case 0x03:
4671
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_LIST;
4672
                break;
4673
            case 0x04:
4674
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_DATE;
4675
                break;
4676
            case 0x05:
4677
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_TIME;
4678
                break;
4679
            case 0x06:
4680
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_TEXTLENGTH;
4681
                break;
4682
            case 0x07:
4683
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_CUSTOM;
4684
                break;
4685
        }
4686
4687
        // bit: 4-6; mask: 0x00000070; error type
4688
        $errorStyle = (0x00000070 & $options) >> 4;
4689 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...
4690
            case 0x00:
4691
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_STOP;
4692
                break;
4693
            case 0x01:
4694
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_WARNING;
4695
                break;
4696
            case 0x02:
4697
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION;
4698
                break;
4699
        }
4700
4701
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
4702
        // I have only seen cases where this is 1
4703
        $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...
4704
4705
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
4706
        $allowBlank = (0x00000100 & $options) >> 8;
4707
4708
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
4709
        $suppressDropDown = (0x00000200 & $options) >> 9;
4710
4711
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
4712
        $showInputMessage = (0x00040000 & $options) >> 18;
4713
4714
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
4715
        $showErrorMessage = (0x00080000 & $options) >> 19;
4716
4717
        // bit: 20-23; mask: 0x00F00000; condition operator
4718
        $operator = (0x00F00000 & $options) >> 20;
4719 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...
4720
            case 0x00:
4721
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_BETWEEN;
4722
                break;
4723
            case 0x01:
4724
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTBETWEEN;
4725
                break;
4726
            case 0x02:
4727
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_EQUAL;
4728
                break;
4729
            case 0x03:
4730
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTEQUAL;
4731
                break;
4732
            case 0x04:
4733
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHAN;
4734
                break;
4735
            case 0x05:
4736
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHAN;
4737
                break;
4738
            case 0x06:
4739
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHANOREQUAL;
4740
                break;
4741
            case 0x07:
4742
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHANOREQUAL;
4743
                break;
4744
        }
4745
4746
        // offset: 4; size: var; title of the prompt box
4747
        $offset = 4;
4748
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4749
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4750
        $offset += $string['size'];
4751
4752
        // offset: var; size: var; title of the error box
4753
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4754
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4755
        $offset += $string['size'];
4756
4757
        // offset: var; size: var; text of the prompt box
4758
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4759
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
4760
        $offset += $string['size'];
4761
4762
        // offset: var; size: var; text of the error box
4763
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4764
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
4765
        $offset += $string['size'];
4766
4767
        // offset: var; size: 2; size of the formula data for the first condition
4768
        $sz1 = self::getInt2d($recordData, $offset);
4769
        $offset += 2;
4770
4771
        // offset: var; size: 2; not used
4772
        $offset += 2;
4773
4774
        // offset: var; size: $sz1; formula data for first condition (without size field)
4775
        $formula1 = substr($recordData, $offset, $sz1);
4776
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
4777
        try {
4778
            $formula1 = $this->getFormulaFromStructure($formula1);
4779
4780
            // in list type validity, null characters are used as item separators
4781
            if ($type == \PhpSpreadsheet\Cell\DataValidation::TYPE_LIST) {
4782
                $formula1 = str_replace(chr(0), ',', $formula1);
4783
            }
4784
        } catch (\PhpSpreadsheet\Exception $e) {
4785
            return;
4786
        }
4787
        $offset += $sz1;
4788
4789
        // offset: var; size: 2; size of the formula data for the first condition
4790
        $sz2 = self::getInt2d($recordData, $offset);
4791
        $offset += 2;
4792
4793
        // offset: var; size: 2; not used
4794
        $offset += 2;
4795
4796
        // offset: var; size: $sz2; formula data for second condition (without size field)
4797
        $formula2 = substr($recordData, $offset, $sz2);
4798
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
4799
        try {
4800
            $formula2 = $this->getFormulaFromStructure($formula2);
4801
        } catch (\PhpSpreadsheet\Exception $e) {
4802
            return;
4803
        }
4804
        $offset += $sz2;
4805
4806
        // offset: var; size: var; cell range address list with
4807
        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
4808
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
4809
4810
        foreach ($cellRangeAddresses as $cellRange) {
4811
            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4812
            foreach (\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($stRange) as $coordinate) {
4813
                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4814
                $objValidation->setType($type);
4815
                $objValidation->setErrorStyle($errorStyle);
4816
                $objValidation->setAllowBlank((bool) $allowBlank);
4817
                $objValidation->setShowInputMessage((bool) $showInputMessage);
4818
                $objValidation->setShowErrorMessage((bool) $showErrorMessage);
4819
                $objValidation->setShowDropDown(!$suppressDropDown);
4820
                $objValidation->setOperator($operator);
4821
                $objValidation->setErrorTitle($errorTitle);
4822
                $objValidation->setError($error);
4823
                $objValidation->setPromptTitle($promptTitle);
4824
                $objValidation->setPrompt($prompt);
4825
                $objValidation->setFormula1($formula1);
4826
                $objValidation->setFormula2($formula2);
4827
            }
4828
        }
4829
    }
4830
4831
    /**
4832
     * Read SHEETLAYOUT record. Stores sheet tab color information.
4833
     */
4834
    private function readSheetLayout()
4835
    {
4836
        $length = self::getInt2d($this->data, $this->pos + 2);
4837
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4838
4839
        // move stream pointer to next record
4840
        $this->pos += 4 + $length;
4841
4842
        // local pointer in record data
4843
        $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...
4844
4845
        if (!$this->readDataOnly) {
4846
            // offset: 0; size: 2; repeated record identifier 0x0862
4847
4848
            // offset: 2; size: 10; not used
4849
4850
            // offset: 12; size: 4; size of record data
4851
            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
4852
            $sz = self::getInt4d($recordData, 12);
4853
4854
            switch ($sz) {
4855
                case 0x14:
4856
                    // offset: 16; size: 2; color index for sheet tab
4857
                    $colorIndex = self::getInt2d($recordData, 16);
4858
                    $color = Excel5\Color::map($colorIndex, $this->palette, $this->version);
4859
                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
4860
                    break;
4861
                case 0x28:
4862
                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
4863
                    return;
4864
                    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...
4865
            }
4866
        }
4867
    }
4868
4869
    /**
4870
     * Read SHEETPROTECTION record (FEATHEADR)
4871
     */
4872
    private function readSheetProtection()
4873
    {
4874
        $length = self::getInt2d($this->data, $this->pos + 2);
4875
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4876
4877
        // move stream pointer to next record
4878
        $this->pos += 4 + $length;
4879
4880
        if ($this->readDataOnly) {
4881
            return;
4882
        }
4883
4884
        // offset: 0; size: 2; repeated record header
4885
4886
        // offset: 2; size: 2; FRT cell reference flag (=0 currently)
4887
4888
        // offset: 4; size: 8; Currently not used and set to 0
4889
4890
        // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
4891
        $isf = self::getInt2d($recordData, 12);
4892
        if ($isf != 2) {
4893
            return;
4894
        }
4895
4896
        // offset: 14; size: 1; =1 since this is a feat header
4897
4898
        // offset: 15; size: 4; size of rgbHdrSData
4899
4900
        // rgbHdrSData, assume "Enhanced Protection"
4901
        // offset: 19; size: 2; option flags
4902
        $options = self::getInt2d($recordData, 19);
4903
4904
        // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
4905
        $bool = (0x0001 & $options) >> 0;
4906
        $this->phpSheet->getProtection()->setObjects(!$bool);
4907
4908
        // bit: 1; mask 0x0002; edit scenarios
4909
        $bool = (0x0002 & $options) >> 1;
4910
        $this->phpSheet->getProtection()->setScenarios(!$bool);
4911
4912
        // bit: 2; mask 0x0004; format cells
4913
        $bool = (0x0004 & $options) >> 2;
4914
        $this->phpSheet->getProtection()->setFormatCells(!$bool);
4915
4916
        // bit: 3; mask 0x0008; format columns
4917
        $bool = (0x0008 & $options) >> 3;
4918
        $this->phpSheet->getProtection()->setFormatColumns(!$bool);
4919
4920
        // bit: 4; mask 0x0010; format rows
4921
        $bool = (0x0010 & $options) >> 4;
4922
        $this->phpSheet->getProtection()->setFormatRows(!$bool);
4923
4924
        // bit: 5; mask 0x0020; insert columns
4925
        $bool = (0x0020 & $options) >> 5;
4926
        $this->phpSheet->getProtection()->setInsertColumns(!$bool);
4927
4928
        // bit: 6; mask 0x0040; insert rows
4929
        $bool = (0x0040 & $options) >> 6;
4930
        $this->phpSheet->getProtection()->setInsertRows(!$bool);
4931
4932
        // bit: 7; mask 0x0080; insert hyperlinks
4933
        $bool = (0x0080 & $options) >> 7;
4934
        $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
4935
4936
        // bit: 8; mask 0x0100; delete columns
4937
        $bool = (0x0100 & $options) >> 8;
4938
        $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
4939
4940
        // bit: 9; mask 0x0200; delete rows
4941
        $bool = (0x0200 & $options) >> 9;
4942
        $this->phpSheet->getProtection()->setDeleteRows(!$bool);
4943
4944
        // bit: 10; mask 0x0400; select locked cells
4945
        $bool = (0x0400 & $options) >> 10;
4946
        $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
4947
4948
        // bit: 11; mask 0x0800; sort cell range
4949
        $bool = (0x0800 & $options) >> 11;
4950
        $this->phpSheet->getProtection()->setSort(!$bool);
4951
4952
        // bit: 12; mask 0x1000; auto filter
4953
        $bool = (0x1000 & $options) >> 12;
4954
        $this->phpSheet->getProtection()->setAutoFilter(!$bool);
4955
4956
        // bit: 13; mask 0x2000; pivot tables
4957
        $bool = (0x2000 & $options) >> 13;
4958
        $this->phpSheet->getProtection()->setPivotTables(!$bool);
4959
4960
        // bit: 14; mask 0x4000; select unlocked cells
4961
        $bool = (0x4000 & $options) >> 14;
4962
        $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
4963
4964
        // offset: 21; size: 2; not used
4965
    }
4966
4967
    /**
4968
     * Read RANGEPROTECTION record
4969
     * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
4970
     * where it is referred to as FEAT record
4971
     */
4972
    private function readRangeProtection()
4973
    {
4974
        $length = self::getInt2d($this->data, $this->pos + 2);
4975
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4976
4977
        // move stream pointer to next record
4978
        $this->pos += 4 + $length;
4979
4980
        // local pointer in record data
4981
        $offset = 0;
4982
4983
        if (!$this->readDataOnly) {
4984
            $offset += 12;
4985
4986
            // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
4987
            $isf = self::getInt2d($recordData, 12);
4988
            if ($isf != 2) {
4989
                // we only read FEAT records of type 2
4990
                return;
4991
            }
4992
            $offset += 2;
4993
4994
            $offset += 5;
4995
4996
            // offset: 19; size: 2; count of ref ranges this feature is on
4997
            $cref = self::getInt2d($recordData, 19);
4998
            $offset += 2;
4999
5000
            $offset += 6;
5001
5002
            // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
5003
            $cellRanges = [];
5004
            for ($i = 0; $i < $cref; ++$i) {
5005
                try {
5006
                    $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
5007
                } catch (\PhpSpreadsheet\Exception $e) {
5008
                    return;
5009
                }
5010
                $cellRanges[] = $cellRange;
5011
                $offset += 8;
5012
            }
5013
5014
            // offset: var; size: var; variable length of feature specific data
5015
            $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...
5016
            $offset += 4;
5017
5018
            // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
5019
            $wPassword = self::getInt4d($recordData, $offset);
5020
            $offset += 4;
5021
5022
            // Apply range protection to sheet
5023
            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...
5024
                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
5025
            }
5026
        }
5027
    }
5028
5029
    /**
5030
     * Read IMDATA record
5031
     */
5032
    private function readImData()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
5033
    {
5034
        $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...
5035
5036
        // get spliced record data
5037
        $splicedRecordData = $this->getSplicedRecordData();
5038
        $recordData = $splicedRecordData['recordData'];
5039
5040
        // UNDER CONSTRUCTION
5041
5042
        // offset: 0; size: 2; image format
5043
        $cf = self::getInt2d($recordData, 0);
5044
5045
        // offset: 2; size: 2; environment from which the file was written
5046
        $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...
5047
5048
        // offset: 4; size: 4; length of the image data
5049
        $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...
5050
5051
        // offset: 8; size: var; image data
5052
        $iData = substr($recordData, 8);
5053
5054
        switch ($cf) {
5055
            case 0x09: // Windows bitmap format
5056
                // BITMAPCOREINFO
5057
                // 1. BITMAPCOREHEADER
5058
                // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
5059
                $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...
5060
    //            var_dump($bcSize);
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...
5061
5062
                // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
5063
                $bcWidth = self::getInt2d($iData, 4);
5064
    //            var_dump($bcWidth);
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...
5065
5066
                // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
5067
                $bcHeight = self::getInt2d($iData, 6);
5068
    //            var_dump($bcHeight);
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...
5069
                $ih = imagecreatetruecolor($bcWidth, $bcHeight);
5070
5071
                // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
5072
5073
                // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
5074
                $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...
5075
    //            var_dump($bcBitCount);
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...
5076
5077
                $rgbString = substr($iData, 12);
5078
                $rgbTriples = [];
5079
                while (strlen($rgbString) > 0) {
5080
                    $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
5081
                    $rgbString = substr($rgbString, 3);
5082
                }
5083
                $x = 0;
5084
                $y = 0;
5085
                foreach ($rgbTriples as $i => $rgbTriple) {
5086
                    $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
5087
                    imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
5088
                    $x = ($x + 1) % $bcWidth;
5089
                    $y = $y + floor(($x + 1) / $bcWidth);
5090
                }
5091
                //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...
5092
5093
                $drawing = new \PhpSpreadsheet\Worksheet\Drawing();
5094
                $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...
5095
                $drawing->setWorksheet($this->phpSheet);
5096
                break;
5097
            case 0x02: // Windows metafile or Macintosh PICT format
5098
            case 0x0e: // native format
5099
            default:
5100
                break;
5101
        }
5102
5103
        // getSplicedRecordData() takes care of moving current position in data stream
5104
    }
5105
5106
    /**
5107
     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
5108
     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
5109
     * In this case, we must treat the CONTINUE record as a MSODRAWING record
5110
     */
5111
    private function readContinue()
5112
    {
5113
        $length = self::getInt2d($this->data, $this->pos + 2);
5114
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5115
5116
        // check if we are reading drawing data
5117
        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
5118
        if ($this->drawingData == '') {
5119
            // move stream pointer to next record
5120
            $this->pos += 4 + $length;
5121
5122
            return;
5123
        }
5124
5125
        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
5126
        if ($length < 4) {
5127
            // move stream pointer to next record
5128
            $this->pos += 4 + $length;
5129
5130
            return;
5131
        }
5132
5133
        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
5134
        // look inside CONTINUE record to see if it looks like a part of an Escher stream
5135
        // we know that Escher stream may be split at least at
5136
        //        0xF003 MsofbtSpgrContainer
5137
        //        0xF004 MsofbtSpContainer
5138
        //        0xF00D MsofbtClientTextbox
5139
        $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
5140
5141
        $splitPoint = self::getInt2d($recordData, 2);
5142
        if (in_array($splitPoint, $validSplitPoints)) {
5143
            // get spliced record data (and move pointer to next record)
5144
            $splicedRecordData = $this->getSplicedRecordData();
5145
            $this->drawingData .= $splicedRecordData['recordData'];
5146
5147
            return;
5148
        }
5149
5150
        // move stream pointer to next record
5151
        $this->pos += 4 + $length;
5152
    }
5153
5154
    /**
5155
     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
5156
     * records are found. Splices the record data pieces and returns the combined string as if record data
5157
     * is in one piece.
5158
     * Moves to next current position in data stream to start of next record different from a CONtINUE record
5159
     *
5160
     * @return array
5161
     */
5162
    private function getSplicedRecordData()
5163
    {
5164
        $data = '';
5165
        $spliceOffsets = [];
5166
5167
        $i = 0;
5168
        $spliceOffsets[0] = 0;
5169
5170
        do {
5171
            ++$i;
5172
5173
            // offset: 0; size: 2; identifier
5174
            $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...
5175
            // offset: 2; size: 2; length
5176
            $length = self::getInt2d($this->data, $this->pos + 2);
5177
            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
5178
5179
            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
5180
5181
            $this->pos += 4 + $length;
5182
            $nextIdentifier = self::getInt2d($this->data, $this->pos);
5183
        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5184
5185
        $splicedData = [
5186
            'recordData' => $data,
5187
            'spliceOffsets' => $spliceOffsets,
5188
        ];
5189
5190
        return $splicedData;
5191
    }
5192
5193
    /**
5194
     * Convert formula structure into human readable Excel formula like 'A3+A5*5'
5195
     *
5196
     * @param string $formulaStructure The complete binary data for the formula
5197
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5198
     * @return string Human readable formula
5199
     */
5200
    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
5201
    {
5202
        // offset: 0; size: 2; size of the following formula data
5203
        $sz = self::getInt2d($formulaStructure, 0);
5204
5205
        // offset: 2; size: sz
5206
        $formulaData = substr($formulaStructure, 2, $sz);
5207
5208
        // for debug: dump the formula data
5209
        //echo '<xmp>';
5210
        //echo 'size: ' . $sz . "\n";
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...
5211
        //echo 'the entire formula data: ';
5212
        //Debug::dump($formulaData);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
5213
        //echo "\n----\n";
5214
5215
        // offset: 2 + sz; size: variable (optional)
5216
        if (strlen($formulaStructure) > 2 + $sz) {
5217
            $additionalData = substr($formulaStructure, 2 + $sz);
5218
5219
            // for debug: dump the additional data
5220
            //echo 'the entire additional data: ';
5221
            //Debug::dump($additionalData);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
5222
            //echo "\n----\n";
5223
        } else {
5224
            $additionalData = '';
5225
        }
5226
5227
        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
5228
    }
5229
5230
    /**
5231
     * Take formula data and additional data for formula and return human readable formula
5232
     *
5233
     * @param string $formulaData The binary data for the formula itself
5234
     * @param string $additionalData Additional binary data going with the formula
5235
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5236
     * @return string Human readable formula
5237
     */
5238
    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
5239
    {
5240
        // start parsing the formula data
5241
        $tokens = [];
5242
5243
        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...
5244
            $tokens[] = $token;
5245
            $formulaData = substr($formulaData, $token['size']);
5246
5247
            // for debug: dump the token
5248
            //var_dump($token);
5249
        }
5250
5251
        $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
0 ignored issues
show
Documentation introduced by
$additionalData is of type string, but the function expects a array.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
5252
5253
        return $formulaString;
5254
    }
5255
5256
    /**
5257
     * Take array of tokens together with additional data for formula and return human readable formula
5258
     *
5259
     * @param array $tokens
5260
     * @param array $additionalData Additional binary data going with the formula
5261
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
0 ignored issues
show
Bug introduced by
There is no parameter named $baseCell. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5262
     * @return string Human readable formula
5263
     */
5264
    private function createFormulaFromTokens($tokens, $additionalData)
5265
    {
5266
        // empty formula?
5267
        if (empty($tokens)) {
5268
            return '';
5269
        }
5270
5271
        $formulaStrings = [];
5272
        foreach ($tokens as $token) {
5273
            // initialize spaces
5274
            $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
5275
            $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
5276
            $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
5277
            $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
5278
            $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
5279
            $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
5280
5281
            switch ($token['name']) {
5282
                case 'tAdd': // addition
5283
                case 'tConcat': // addition
5284
                case 'tDiv': // division
5285
                case 'tEQ': // equality
5286
                case 'tGE': // greater than or equal
5287
                case 'tGT': // greater than
5288
                case 'tIsect': // intersection
5289
                case 'tLE': // less than or equal
5290
                case 'tList': // less than or equal
5291
                case 'tLT': // less than
5292
                case 'tMul': // multiplication
5293
                case 'tNE': // multiplication
5294
                case 'tPower': // power
5295
                case 'tRange': // range
5296
                case 'tSub': // subtraction
5297
                    $op2 = array_pop($formulaStrings);
5298
                    $op1 = array_pop($formulaStrings);
5299
                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
5300
                    unset($space0, $space1);
5301
                    break;
5302
                case 'tUplus': // unary plus
5303 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...
5304
                    $op = array_pop($formulaStrings);
5305
                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
5306
                    unset($space0, $space1);
5307
                    break;
5308 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...
5309
                    $op = array_pop($formulaStrings);
5310
                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
5311
                    unset($space0, $space1);
5312
                    break;
5313
                case 'tAttrVolatile': // indicates volatile function
5314
                case 'tAttrIf':
5315
                case 'tAttrSkip':
5316
                case 'tAttrChoose':
5317
                    // token is only important for Excel formula evaluator
5318
                    // do nothing
5319
                    break;
5320
                case 'tAttrSpace': // space / carriage return
5321
                    // space will be used when next token arrives, do not alter formulaString stack
5322
                    switch ($token['data']['spacetype']) {
5323
                        case 'type0':
5324
                            $space0 = str_repeat(' ', $token['data']['spacecount']);
5325
                            break;
5326
                        case 'type1':
5327
                            $space1 = str_repeat("\n", $token['data']['spacecount']);
5328
                            break;
5329
                        case 'type2':
5330
                            $space2 = str_repeat(' ', $token['data']['spacecount']);
5331
                            break;
5332
                        case 'type3':
5333
                            $space3 = str_repeat("\n", $token['data']['spacecount']);
5334
                            break;
5335
                        case 'type4':
5336
                            $space4 = str_repeat(' ', $token['data']['spacecount']);
5337
                            break;
5338
                        case 'type5':
5339
                            $space5 = str_repeat("\n", $token['data']['spacecount']);
5340
                            break;
5341
                    }
5342
                    break;
5343
                case 'tAttrSum': // SUM function with one parameter
5344
                    $op = array_pop($formulaStrings);
5345
                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
5346
                    unset($space0, $space1);
5347
                    break;
5348
                case 'tFunc': // function with fixed number of arguments
5349
                case 'tFuncV': // function with variable number of arguments
5350
                    if ($token['data']['function'] != '') {
5351
                        // normal function
5352
                        $ops = []; // array of operators
5353 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...
5354
                            $ops[] = array_pop($formulaStrings);
5355
                        }
5356
                        $ops = array_reverse($ops);
5357
                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
5358
                        unset($space0, $space1);
5359
                    } else {
5360
                        // add-in function
5361
                        $ops = []; // array of operators
5362 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...
5363
                            $ops[] = array_pop($formulaStrings);
5364
                        }
5365
                        $ops = array_reverse($ops);
5366
                        $function = array_pop($formulaStrings);
5367
                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
5368
                        unset($space0, $space1);
5369
                    }
5370
                    break;
5371
                case 'tParen': // parenthesis
5372
                    $expression = array_pop($formulaStrings);
5373
                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
5374
                    unset($space2, $space3, $space4, $space5);
5375
                    break;
5376
                case 'tArray': // array constant
5377
                    $constantArray = self::_readBIFF8ConstantArray($additionalData);
0 ignored issues
show
Bug introduced by
The method _readBIFF8ConstantArray() does not exist on PhpSpreadsheet\Reader\Excel5. Did you maybe mean readBIFF8ConstantArray()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
5378
                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
5379
                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
5380
                    unset($space0, $space1);
5381
                    break;
5382
                case 'tMemArea':
5383
                    // bite off chunk of additional data
5384
                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
0 ignored issues
show
Bug introduced by
It seems like $additionalData defined by parameter $additionalData on line 5264 can also be of type array; however, PhpSpreadsheet\Reader\Ex...8CellRangeAddressList() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
5385
                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
5386
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5387
                    unset($space0, $space1);
5388
                    break;
5389
                case 'tArea': // cell range address
5390
                case 'tBool': // boolean
5391
                case 'tErr': // error code
5392
                case 'tInt': // integer
5393
                case 'tMemErr':
5394
                case 'tMemFunc':
5395
                case 'tMissArg':
5396
                case 'tName':
5397
                case 'tNameX':
5398
                case 'tNum': // number
5399
                case 'tRef': // single cell reference
5400
                case 'tRef3d': // 3d cell reference
5401
                case 'tArea3d': // 3d cell range reference
5402
                case 'tRefN':
5403
                case 'tAreaN':
5404
                case 'tStr': // string
5405
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5406
                    unset($space0, $space1);
5407
                    break;
5408
            }
5409
        }
5410
        $formulaString = $formulaStrings[0];
5411
5412
        // for debug: dump the human readable formula
5413
        //echo '----' . "\n";
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
5414
        //echo 'Formula: ' . $formulaString;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
5415
5416
        return $formulaString;
5417
    }
5418
5419
    /**
5420
     * Fetch next token from binary formula data
5421
     *
5422
     * @param string Formula data
5423
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5424
     * @throws Exception
5425
     * @return array
5426
     */
5427
    private function getNextToken($formulaData, $baseCell = 'A1')
5428
    {
5429
        // offset: 0; size: 1; token id
5430
        $id = ord($formulaData[0]); // token id
5431
        $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...
5432
5433
        switch ($id) {
5434
            case 0x03:
5435
                $name = 'tAdd';
5436
                $size = 1;
5437
                $data = '+';
5438
                break;
5439
            case 0x04:
5440
                $name = 'tSub';
5441
                $size = 1;
5442
                $data = '-';
5443
                break;
5444
            case 0x05:
5445
                $name = 'tMul';
5446
                $size = 1;
5447
                $data = '*';
5448
                break;
5449
            case 0x06:
5450
                $name = 'tDiv';
5451
                $size = 1;
5452
                $data = '/';
5453
                break;
5454
            case 0x07:
5455
                $name = 'tPower';
5456
                $size = 1;
5457
                $data = '^';
5458
                break;
5459
            case 0x08:
5460
                $name = 'tConcat';
5461
                $size = 1;
5462
                $data = '&';
5463
                break;
5464
            case 0x09:
5465
                $name = 'tLT';
5466
                $size = 1;
5467
                $data = '<';
5468
                break;
5469
            case 0x0A:
5470
                $name = 'tLE';
5471
                $size = 1;
5472
                $data = '<=';
5473
                break;
5474
            case 0x0B:
5475
                $name = 'tEQ';
5476
                $size = 1;
5477
                $data = '=';
5478
                break;
5479
            case 0x0C:
5480
                $name = 'tGE';
5481
                $size = 1;
5482
                $data = '>=';
5483
                break;
5484
            case 0x0D:
5485
                $name = 'tGT';
5486
                $size = 1;
5487
                $data = '>';
5488
                break;
5489
            case 0x0E:
5490
                $name = 'tNE';
5491
                $size = 1;
5492
                $data = '<>';
5493
                break;
5494
            case 0x0F:
5495
                $name = 'tIsect';
5496
                $size = 1;
5497
                $data = ' ';
5498
                break;
5499
            case 0x10:
5500
                $name = 'tList';
5501
                $size = 1;
5502
                $data = ',';
5503
                break;
5504
            case 0x11:
5505
                $name = 'tRange';
5506
                $size = 1;
5507
                $data = ':';
5508
                break;
5509
            case 0x12:
5510
                $name = 'tUplus';
5511
                $size = 1;
5512
                $data = '+';
5513
                break;
5514
            case 0x13:
5515
                $name = 'tUminus';
5516
                $size = 1;
5517
                $data = '-';
5518
                break;
5519
            case 0x14:
5520
                $name = 'tPercent';
5521
                $size = 1;
5522
                $data = '%';
5523
                break;
5524
            case 0x15:    //    parenthesis
5525
                $name = 'tParen';
5526
                $size = 1;
5527
                $data = null;
5528
                break;
5529
            case 0x16:    //    missing argument
5530
                $name = 'tMissArg';
5531
                $size = 1;
5532
                $data = '';
5533
                break;
5534
            case 0x17:    //    string
5535
                $name = 'tStr';
5536
                // offset: 1; size: var; Unicode string, 8-bit string length
5537
                $string = self::readUnicodeStringShort(substr($formulaData, 1));
5538
                $size = 1 + $string['size'];
5539
                $data = self::UTF8toExcelDoubleQuoted($string['value']);
5540
                break;
5541
            case 0x19:    //    Special attribute
5542
                // offset: 1; size: 1; attribute type flags:
5543
                switch (ord($formulaData[1])) {
5544
                    case 0x01:
5545
                        $name = 'tAttrVolatile';
5546
                        $size = 4;
5547
                        $data = null;
5548
                        break;
5549
                    case 0x02:
5550
                        $name = 'tAttrIf';
5551
                        $size = 4;
5552
                        $data = null;
5553
                        break;
5554
                    case 0x04:
5555
                        $name = 'tAttrChoose';
5556
                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
5557
                        $nc = self::getInt2d($formulaData, 2);
5558
                        // offset: 4; size: 2 * $nc
5559
                        // offset: 4 + 2 * $nc; size: 2
5560
                        $size = 2 * $nc + 6;
5561
                        $data = null;
5562
                        break;
5563
                    case 0x08:
5564
                        $name = 'tAttrSkip';
5565
                        $size = 4;
5566
                        $data = null;
5567
                        break;
5568
                    case 0x10:
5569
                        $name = 'tAttrSum';
5570
                        $size = 4;
5571
                        $data = null;
5572
                        break;
5573
                    case 0x40:
5574
                    case 0x41:
5575
                        $name = 'tAttrSpace';
5576
                        $size = 4;
5577
                        // offset: 2; size: 2; space type and position
5578
                        switch (ord($formulaData[2])) {
5579
                            case 0x00:
5580
                                $spacetype = 'type0';
5581
                                break;
5582
                            case 0x01:
5583
                                $spacetype = 'type1';
5584
                                break;
5585
                            case 0x02:
5586
                                $spacetype = 'type2';
5587
                                break;
5588
                            case 0x03:
5589
                                $spacetype = 'type3';
5590
                                break;
5591
                            case 0x04:
5592
                                $spacetype = 'type4';
5593
                                break;
5594
                            case 0x05:
5595
                                $spacetype = 'type5';
5596
                                break;
5597
                            default:
5598
                                throw new Exception('Unrecognized space type in tAttrSpace token');
5599
                                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...
5600
                        }
5601
                        // offset: 3; size: 1; number of inserted spaces/carriage returns
5602
                        $spacecount = ord($formulaData[3]);
5603
5604
                        $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
5605
                        break;
5606
                    default:
5607
                        throw new Exception('Unrecognized attribute flag in tAttr token');
5608
                        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...
5609
                }
5610
                break;
5611
            case 0x1C:    //    error code
5612
                // offset: 1; size: 1; error code
5613
                $name = 'tErr';
5614
                $size = 2;
5615
                $data = Excel5\ErrorCode::lookup(ord($formulaData[1]));
5616
                break;
5617
            case 0x1D:    //    boolean
5618
                // 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...
5619
                $name = 'tBool';
5620
                $size = 2;
5621
                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
5622
                break;
5623
            case 0x1E:    //    integer
5624
                // offset: 1; size: 2; unsigned 16-bit integer
5625
                $name = 'tInt';
5626
                $size = 3;
5627
                $data = self::getInt2d($formulaData, 1);
5628
                break;
5629
            case 0x1F:    //    number
5630
                // 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...
5631
                $name = 'tNum';
5632
                $size = 9;
5633
                $data = self::extractNumber(substr($formulaData, 1));
5634
                $data = str_replace(',', '.', (string) $data); // in case non-English locale
5635
                break;
5636
            case 0x20:    //    array constant
5637
            case 0x40:
5638
            case 0x60:
5639
                // offset: 1; size: 7; not used
5640
                $name = 'tArray';
5641
                $size = 8;
5642
                $data = null;
5643
                break;
5644
            case 0x21:    //    function with fixed number of arguments
5645
            case 0x41:
5646
            case 0x61:
5647
                $name = 'tFunc';
5648
                $size = 3;
5649
                // offset: 1; size: 2; index to built-in sheet function
5650
                switch (self::getInt2d($formulaData, 1)) {
5651
                    case 2:
5652
                        $function = 'ISNA';
5653
                        $args = 1;
5654
                        break;
5655
                    case 3:
5656
                        $function = 'ISERROR';
5657
                        $args = 1;
5658
                        break;
5659
                    case 10:
5660
                        $function = 'NA';
5661
                        $args = 0;
5662
                        break;
5663
                    case 15:
5664
                        $function = 'SIN';
5665
                        $args = 1;
5666
                        break;
5667
                    case 16:
5668
                        $function = 'COS';
5669
                        $args = 1;
5670
                        break;
5671
                    case 17:
5672
                        $function = 'TAN';
5673
                        $args = 1;
5674
                        break;
5675
                    case 18:
5676
                        $function = 'ATAN';
5677
                        $args = 1;
5678
                        break;
5679
                    case 19:
5680
                        $function = 'PI';
5681
                        $args = 0;
5682
                        break;
5683
                    case 20:
5684
                        $function = 'SQRT';
5685
                        $args = 1;
5686
                        break;
5687
                    case 21:
5688
                        $function = 'EXP';
5689
                        $args = 1;
5690
                        break;
5691
                    case 22:
5692
                        $function = 'LN';
5693
                        $args = 1;
5694
                        break;
5695
                    case 23:
5696
                        $function = 'LOG10';
5697
                        $args = 1;
5698
                        break;
5699
                    case 24:
5700
                        $function = 'ABS';
5701
                        $args = 1;
5702
                        break;
5703
                    case 25:
5704
                        $function = 'INT';
5705
                        $args = 1;
5706
                        break;
5707
                    case 26:
5708
                        $function = 'SIGN';
5709
                        $args = 1;
5710
                        break;
5711
                    case 27:
5712
                        $function = 'ROUND';
5713
                        $args = 2;
5714
                        break;
5715
                    case 30:
5716
                        $function = 'REPT';
5717
                        $args = 2;
5718
                        break;
5719
                    case 31:
5720
                        $function = 'MID';
5721
                        $args = 3;
5722
                        break;
5723
                    case 32:
5724
                        $function = 'LEN';
5725
                        $args = 1;
5726
                        break;
5727
                    case 33:
5728
                        $function = 'VALUE';
5729
                        $args = 1;
5730
                        break;
5731
                    case 34:
5732
                        $function = 'TRUE';
5733
                        $args = 0;
5734
                        break;
5735
                    case 35:
5736
                        $function = 'FALSE';
5737
                        $args = 0;
5738
                        break;
5739
                    case 38:
5740
                        $function = 'NOT';
5741
                        $args = 1;
5742
                        break;
5743
                    case 39:
5744
                        $function = 'MOD';
5745
                        $args = 2;
5746
                        break;
5747
                    case 40:
5748
                        $function = 'DCOUNT';
5749
                        $args = 3;
5750
                        break;
5751
                    case 41:
5752
                        $function = 'DSUM';
5753
                        $args = 3;
5754
                        break;
5755
                    case 42:
5756
                        $function = 'DAVERAGE';
5757
                        $args = 3;
5758
                        break;
5759
                    case 43:
5760
                        $function = 'DMIN';
5761
                        $args = 3;
5762
                        break;
5763
                    case 44:
5764
                        $function = 'DMAX';
5765
                        $args = 3;
5766
                        break;
5767
                    case 45:
5768
                        $function = 'DSTDEV';
5769
                        $args = 3;
5770
                        break;
5771
                    case 48:
5772
                        $function = 'TEXT';
5773
                        $args = 2;
5774
                        break;
5775
                    case 61:
5776
                        $function = 'MIRR';
5777
                        $args = 3;
5778
                        break;
5779
                    case 63:
5780
                        $function = 'RAND';
5781
                        $args = 0;
5782
                        break;
5783
                    case 65:
5784
                        $function = 'DATE';
5785
                        $args = 3;
5786
                        break;
5787
                    case 66:
5788
                        $function = 'TIME';
5789
                        $args = 3;
5790
                        break;
5791
                    case 67:
5792
                        $function = 'DAY';
5793
                        $args = 1;
5794
                        break;
5795
                    case 68:
5796
                        $function = 'MONTH';
5797
                        $args = 1;
5798
                        break;
5799
                    case 69:
5800
                        $function = 'YEAR';
5801
                        $args = 1;
5802
                        break;
5803
                    case 71:
5804
                        $function = 'HOUR';
5805
                        $args = 1;
5806
                        break;
5807
                    case 72:
5808
                        $function = 'MINUTE';
5809
                        $args = 1;
5810
                        break;
5811
                    case 73:
5812
                        $function = 'SECOND';
5813
                        $args = 1;
5814
                        break;
5815
                    case 74:
5816
                        $function = 'NOW';
5817
                        $args = 0;
5818
                        break;
5819
                    case 75:
5820
                        $function = 'AREAS';
5821
                        $args = 1;
5822
                        break;
5823
                    case 76:
5824
                        $function = 'ROWS';
5825
                        $args = 1;
5826
                        break;
5827
                    case 77:
5828
                        $function = 'COLUMNS';
5829
                        $args = 1;
5830
                        break;
5831
                    case 83:
5832
                        $function = 'TRANSPOSE';
5833
                        $args = 1;
5834
                        break;
5835
                    case 86:
5836
                        $function = 'TYPE';
5837
                        $args = 1;
5838
                        break;
5839
                    case 97:
5840
                        $function = 'ATAN2';
5841
                        $args = 2;
5842
                        break;
5843
                    case 98:
5844
                        $function = 'ASIN';
5845
                        $args = 1;
5846
                        break;
5847
                    case 99:
5848
                        $function = 'ACOS';
5849
                        $args = 1;
5850
                        break;
5851
                    case 105:
5852
                        $function = 'ISREF';
5853
                        $args = 1;
5854
                        break;
5855
                    case 111:
5856
                        $function = 'CHAR';
5857
                        $args = 1;
5858
                        break;
5859
                    case 112:
5860
                        $function = 'LOWER';
5861
                        $args = 1;
5862
                        break;
5863
                    case 113:
5864
                        $function = 'UPPER';
5865
                        $args = 1;
5866
                        break;
5867
                    case 114:
5868
                        $function = 'PROPER';
5869
                        $args = 1;
5870
                        break;
5871
                    case 117:
5872
                        $function = 'EXACT';
5873
                        $args = 2;
5874
                        break;
5875
                    case 118:
5876
                        $function = 'TRIM';
5877
                        $args = 1;
5878
                        break;
5879
                    case 119:
5880
                        $function = 'REPLACE';
5881
                        $args = 4;
5882
                        break;
5883
                    case 121:
5884
                        $function = 'CODE';
5885
                        $args = 1;
5886
                        break;
5887
                    case 126:
5888
                        $function = 'ISERR';
5889
                        $args = 1;
5890
                        break;
5891
                    case 127:
5892
                        $function = 'ISTEXT';
5893
                        $args = 1;
5894
                        break;
5895
                    case 128:
5896
                        $function = 'ISNUMBER';
5897
                        $args = 1;
5898
                        break;
5899
                    case 129:
5900
                        $function = 'ISBLANK';
5901
                        $args = 1;
5902
                        break;
5903
                    case 130:
5904
                        $function = 'T';
5905
                        $args = 1;
5906
                        break;
5907
                    case 131:
5908
                        $function = 'N';
5909
                        $args = 1;
5910
                        break;
5911
                    case 140:
5912
                        $function = 'DATEVALUE';
5913
                        $args = 1;
5914
                        break;
5915
                    case 141:
5916
                        $function = 'TIMEVALUE';
5917
                        $args = 1;
5918
                        break;
5919
                    case 142:
5920
                        $function = 'SLN';
5921
                        $args = 3;
5922
                        break;
5923
                    case 143:
5924
                        $function = 'SYD';
5925
                        $args = 4;
5926
                        break;
5927
                    case 162:
5928
                        $function = 'CLEAN';
5929
                        $args = 1;
5930
                        break;
5931
                    case 163:
5932
                        $function = 'MDETERM';
5933
                        $args = 1;
5934
                        break;
5935
                    case 164:
5936
                        $function = 'MINVERSE';
5937
                        $args = 1;
5938
                        break;
5939
                    case 165:
5940
                        $function = 'MMULT';
5941
                        $args = 2;
5942
                        break;
5943
                    case 184:
5944
                        $function = 'FACT';
5945
                        $args = 1;
5946
                        break;
5947
                    case 189:
5948
                        $function = 'DPRODUCT';
5949
                        $args = 3;
5950
                        break;
5951
                    case 190:
5952
                        $function = 'ISNONTEXT';
5953
                        $args = 1;
5954
                        break;
5955
                    case 195:
5956
                        $function = 'DSTDEVP';
5957
                        $args = 3;
5958
                        break;
5959
                    case 196:
5960
                        $function = 'DVARP';
5961
                        $args = 3;
5962
                        break;
5963
                    case 198:
5964
                        $function = 'ISLOGICAL';
5965
                        $args = 1;
5966
                        break;
5967
                    case 199:
5968
                        $function = 'DCOUNTA';
5969
                        $args = 3;
5970
                        break;
5971
                    case 207:
5972
                        $function = 'REPLACEB';
5973
                        $args = 4;
5974
                        break;
5975
                    case 210:
5976
                        $function = 'MIDB';
5977
                        $args = 3;
5978
                        break;
5979
                    case 211:
5980
                        $function = 'LENB';
5981
                        $args = 1;
5982
                        break;
5983
                    case 212:
5984
                        $function = 'ROUNDUP';
5985
                        $args = 2;
5986
                        break;
5987
                    case 213:
5988
                        $function = 'ROUNDDOWN';
5989
                        $args = 2;
5990
                        break;
5991
                    case 214:
5992
                        $function = 'ASC';
5993
                        $args = 1;
5994
                        break;
5995
                    case 215:
5996
                        $function = 'DBCS';
5997
                        $args = 1;
5998
                        break;
5999
                    case 221:
6000
                        $function = 'TODAY';
6001
                        $args = 0;
6002
                        break;
6003
                    case 229:
6004
                        $function = 'SINH';
6005
                        $args = 1;
6006
                        break;
6007
                    case 230:
6008
                        $function = 'COSH';
6009
                        $args = 1;
6010
                        break;
6011
                    case 231:
6012
                        $function = 'TANH';
6013
                        $args = 1;
6014
                        break;
6015
                    case 232:
6016
                        $function = 'ASINH';
6017
                        $args = 1;
6018
                        break;
6019
                    case 233:
6020
                        $function = 'ACOSH';
6021
                        $args = 1;
6022
                        break;
6023
                    case 234:
6024
                        $function = 'ATANH';
6025
                        $args = 1;
6026
                        break;
6027
                    case 235:
6028
                        $function = 'DGET';
6029
                        $args = 3;
6030
                        break;
6031
                    case 244:
6032
                        $function = 'INFO';
6033
                        $args = 1;
6034
                        break;
6035
                    case 252:
6036
                        $function = 'FREQUENCY';
6037
                        $args = 2;
6038
                        break;
6039
                    case 261:
6040
                        $function = 'ERROR.TYPE';
6041
                        $args = 1;
6042
                        break;
6043
                    case 271:
6044
                        $function = 'GAMMALN';
6045
                        $args = 1;
6046
                        break;
6047
                    case 273:
6048
                        $function = 'BINOMDIST';
6049
                        $args = 4;
6050
                        break;
6051
                    case 274:
6052
                        $function = 'CHIDIST';
6053
                        $args = 2;
6054
                        break;
6055
                    case 275:
6056
                        $function = 'CHIINV';
6057
                        $args = 2;
6058
                        break;
6059
                    case 276:
6060
                        $function = 'COMBIN';
6061
                        $args = 2;
6062
                        break;
6063
                    case 277:
6064
                        $function = 'CONFIDENCE';
6065
                        $args = 3;
6066
                        break;
6067
                    case 278:
6068
                        $function = 'CRITBINOM';
6069
                        $args = 3;
6070
                        break;
6071
                    case 279:
6072
                        $function = 'EVEN';
6073
                        $args = 1;
6074
                        break;
6075
                    case 280:
6076
                        $function = 'EXPONDIST';
6077
                        $args = 3;
6078
                        break;
6079
                    case 281:
6080
                        $function = 'FDIST';
6081
                        $args = 3;
6082
                        break;
6083
                    case 282:
6084
                        $function = 'FINV';
6085
                        $args = 3;
6086
                        break;
6087
                    case 283:
6088
                        $function = 'FISHER';
6089
                        $args = 1;
6090
                        break;
6091
                    case 284:
6092
                        $function = 'FISHERINV';
6093
                        $args = 1;
6094
                        break;
6095
                    case 285:
6096
                        $function = 'FLOOR';
6097
                        $args = 2;
6098
                        break;
6099
                    case 286:
6100
                        $function = 'GAMMADIST';
6101
                        $args = 4;
6102
                        break;
6103
                    case 287:
6104
                        $function = 'GAMMAINV';
6105
                        $args = 3;
6106
                        break;
6107
                    case 288:
6108
                        $function = 'CEILING';
6109
                        $args = 2;
6110
                        break;
6111
                    case 289:
6112
                        $function = 'HYPGEOMDIST';
6113
                        $args = 4;
6114
                        break;
6115
                    case 290:
6116
                        $function = 'LOGNORMDIST';
6117
                        $args = 3;
6118
                        break;
6119
                    case 291:
6120
                        $function = 'LOGINV';
6121
                        $args = 3;
6122
                        break;
6123
                    case 292:
6124
                        $function = 'NEGBINOMDIST';
6125
                        $args = 3;
6126
                        break;
6127
                    case 293:
6128
                        $function = 'NORMDIST';
6129
                        $args = 4;
6130
                        break;
6131
                    case 294:
6132
                        $function = 'NORMSDIST';
6133
                        $args = 1;
6134
                        break;
6135
                    case 295:
6136
                        $function = 'NORMINV';
6137
                        $args = 3;
6138
                        break;
6139
                    case 296:
6140
                        $function = 'NORMSINV';
6141
                        $args = 1;
6142
                        break;
6143
                    case 297:
6144
                        $function = 'STANDARDIZE';
6145
                        $args = 3;
6146
                        break;
6147
                    case 298:
6148
                        $function = 'ODD';
6149
                        $args = 1;
6150
                        break;
6151
                    case 299:
6152
                        $function = 'PERMUT';
6153
                        $args = 2;
6154
                        break;
6155
                    case 300:
6156
                        $function = 'POISSON';
6157
                        $args = 3;
6158
                        break;
6159
                    case 301:
6160
                        $function = 'TDIST';
6161
                        $args = 3;
6162
                        break;
6163
                    case 302:
6164
                        $function = 'WEIBULL';
6165
                        $args = 4;
6166
                        break;
6167
                    case 303:
6168
                        $function = 'SUMXMY2';
6169
                        $args = 2;
6170
                        break;
6171
                    case 304:
6172
                        $function = 'SUMX2MY2';
6173
                        $args = 2;
6174
                        break;
6175
                    case 305:
6176
                        $function = 'SUMX2PY2';
6177
                        $args = 2;
6178
                        break;
6179
                    case 306:
6180
                        $function = 'CHITEST';
6181
                        $args = 2;
6182
                        break;
6183
                    case 307:
6184
                        $function = 'CORREL';
6185
                        $args = 2;
6186
                        break;
6187
                    case 308:
6188
                        $function = 'COVAR';
6189
                        $args = 2;
6190
                        break;
6191
                    case 309:
6192
                        $function = 'FORECAST';
6193
                        $args = 3;
6194
                        break;
6195
                    case 310:
6196
                        $function = 'FTEST';
6197
                        $args = 2;
6198
                        break;
6199
                    case 311:
6200
                        $function = 'INTERCEPT';
6201
                        $args = 2;
6202
                        break;
6203
                    case 312:
6204
                        $function = 'PEARSON';
6205
                        $args = 2;
6206
                        break;
6207
                    case 313:
6208
                        $function = 'RSQ';
6209
                        $args = 2;
6210
                        break;
6211
                    case 314:
6212
                        $function = 'STEYX';
6213
                        $args = 2;
6214
                        break;
6215
                    case 315:
6216
                        $function = 'SLOPE';
6217
                        $args = 2;
6218
                        break;
6219
                    case 316:
6220
                        $function = 'TTEST';
6221
                        $args = 4;
6222
                        break;
6223
                    case 325:
6224
                        $function = 'LARGE';
6225
                        $args = 2;
6226
                        break;
6227
                    case 326:
6228
                        $function = 'SMALL';
6229
                        $args = 2;
6230
                        break;
6231
                    case 327:
6232
                        $function = 'QUARTILE';
6233
                        $args = 2;
6234
                        break;
6235
                    case 328:
6236
                        $function = 'PERCENTILE';
6237
                        $args = 2;
6238
                        break;
6239
                    case 331:
6240
                        $function = 'TRIMMEAN';
6241
                        $args = 2;
6242
                        break;
6243
                    case 332:
6244
                        $function = 'TINV';
6245
                        $args = 2;
6246
                        break;
6247
                    case 337:
6248
                        $function = 'POWER';
6249
                        $args = 2;
6250
                        break;
6251
                    case 342:
6252
                        $function = 'RADIANS';
6253
                        $args = 1;
6254
                        break;
6255
                    case 343:
6256
                        $function = 'DEGREES';
6257
                        $args = 1;
6258
                        break;
6259
                    case 346:
6260
                        $function = 'COUNTIF';
6261
                        $args = 2;
6262
                        break;
6263
                    case 347:
6264
                        $function = 'COUNTBLANK';
6265
                        $args = 1;
6266
                        break;
6267
                    case 350:
6268
                        $function = 'ISPMT';
6269
                        $args = 4;
6270
                        break;
6271
                    case 351:
6272
                        $function = 'DATEDIF';
6273
                        $args = 3;
6274
                        break;
6275
                    case 352:
6276
                        $function = 'DATESTRING';
6277
                        $args = 1;
6278
                        break;
6279
                    case 353:
6280
                        $function = 'NUMBERSTRING';
6281
                        $args = 2;
6282
                        break;
6283
                    case 360:
6284
                        $function = 'PHONETIC';
6285
                        $args = 1;
6286
                        break;
6287
                    case 368:
6288
                        $function = 'BAHTTEXT';
6289
                        $args = 1;
6290
                        break;
6291
                    default:
6292
                        throw new Exception('Unrecognized function in formula');
6293
                        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...
6294
                }
6295
                $data = ['function' => $function, 'args' => $args];
6296
                break;
6297
            case 0x22:    //    function with variable number of arguments
6298
            case 0x42:
6299
            case 0x62:
6300
                $name = 'tFuncV';
6301
                $size = 4;
6302
                // offset: 1; size: 1; number of arguments
6303
                $args = ord($formulaData[1]);
6304
                // offset: 2: size: 2; index to built-in sheet function
6305
                $index = self::getInt2d($formulaData, 2);
6306
                switch ($index) {
6307
                    case 0:
6308
                        $function = 'COUNT';
6309
                        break;
6310
                    case 1:
6311
                        $function = 'IF';
6312
                        break;
6313
                    case 4:
6314
                        $function = 'SUM';
6315
                        break;
6316
                    case 5:
6317
                        $function = 'AVERAGE';
6318
                        break;
6319
                    case 6:
6320
                        $function = 'MIN';
6321
                        break;
6322
                    case 7:
6323
                        $function = 'MAX';
6324
                        break;
6325
                    case 8:
6326
                        $function = 'ROW';
6327
                        break;
6328
                    case 9:
6329
                        $function = 'COLUMN';
6330
                        break;
6331
                    case 11:
6332
                        $function = 'NPV';
6333
                        break;
6334
                    case 12:
6335
                        $function = 'STDEV';
6336
                        break;
6337
                    case 13:
6338
                        $function = 'DOLLAR';
6339
                        break;
6340
                    case 14:
6341
                        $function = 'FIXED';
6342
                        break;
6343
                    case 28:
6344
                        $function = 'LOOKUP';
6345
                        break;
6346
                    case 29:
6347
                        $function = 'INDEX';
6348
                        break;
6349
                    case 36:
6350
                        $function = 'AND';
6351
                        break;
6352
                    case 37:
6353
                        $function = 'OR';
6354
                        break;
6355
                    case 46:
6356
                        $function = 'VAR';
6357
                        break;
6358
                    case 49:
6359
                        $function = 'LINEST';
6360
                        break;
6361
                    case 50:
6362
                        $function = 'TREND';
6363
                        break;
6364
                    case 51:
6365
                        $function = 'LOGEST';
6366
                        break;
6367
                    case 52:
6368
                        $function = 'GROWTH';
6369
                        break;
6370
                    case 56:
6371
                        $function = 'PV';
6372
                        break;
6373
                    case 57:
6374
                        $function = 'FV';
6375
                        break;
6376
                    case 58:
6377
                        $function = 'NPER';
6378
                        break;
6379
                    case 59:
6380
                        $function = 'PMT';
6381
                        break;
6382
                    case 60:
6383
                        $function = 'RATE';
6384
                        break;
6385
                    case 62:
6386
                        $function = 'IRR';
6387
                        break;
6388
                    case 64:
6389
                        $function = 'MATCH';
6390
                        break;
6391
                    case 70:
6392
                        $function = 'WEEKDAY';
6393
                        break;
6394
                    case 78:
6395
                        $function = 'OFFSET';
6396
                        break;
6397
                    case 82:
6398
                        $function = 'SEARCH';
6399
                        break;
6400
                    case 100:
6401
                        $function = 'CHOOSE';
6402
                        break;
6403
                    case 101:
6404
                        $function = 'HLOOKUP';
6405
                        break;
6406
                    case 102:
6407
                        $function = 'VLOOKUP';
6408
                        break;
6409
                    case 109:
6410
                        $function = 'LOG';
6411
                        break;
6412
                    case 115:
6413
                        $function = 'LEFT';
6414
                        break;
6415
                    case 116:
6416
                        $function = 'RIGHT';
6417
                        break;
6418
                    case 120:
6419
                        $function = 'SUBSTITUTE';
6420
                        break;
6421
                    case 124:
6422
                        $function = 'FIND';
6423
                        break;
6424
                    case 125:
6425
                        $function = 'CELL';
6426
                        break;
6427
                    case 144:
6428
                        $function = 'DDB';
6429
                        break;
6430
                    case 148:
6431
                        $function = 'INDIRECT';
6432
                        break;
6433
                    case 167:
6434
                        $function = 'IPMT';
6435
                        break;
6436
                    case 168:
6437
                        $function = 'PPMT';
6438
                        break;
6439
                    case 169:
6440
                        $function = 'COUNTA';
6441
                        break;
6442
                    case 183:
6443
                        $function = 'PRODUCT';
6444
                        break;
6445
                    case 193:
6446
                        $function = 'STDEVP';
6447
                        break;
6448
                    case 194:
6449
                        $function = 'VARP';
6450
                        break;
6451
                    case 197:
6452
                        $function = 'TRUNC';
6453
                        break;
6454
                    case 204:
6455
                        $function = 'USDOLLAR';
6456
                        break;
6457
                    case 205:
6458
                        $function = 'FINDB';
6459
                        break;
6460
                    case 206:
6461
                        $function = 'SEARCHB';
6462
                        break;
6463
                    case 208:
6464
                        $function = 'LEFTB';
6465
                        break;
6466
                    case 209:
6467
                        $function = 'RIGHTB';
6468
                        break;
6469
                    case 216:
6470
                        $function = 'RANK';
6471
                        break;
6472
                    case 219:
6473
                        $function = 'ADDRESS';
6474
                        break;
6475
                    case 220:
6476
                        $function = 'DAYS360';
6477
                        break;
6478
                    case 222:
6479
                        $function = 'VDB';
6480
                        break;
6481
                    case 227:
6482
                        $function = 'MEDIAN';
6483
                        break;
6484
                    case 228:
6485
                        $function = 'SUMPRODUCT';
6486
                        break;
6487
                    case 247:
6488
                        $function = 'DB';
6489
                        break;
6490
                    case 255:
6491
                        $function = '';
6492
                        break;
6493
                    case 269:
6494
                        $function = 'AVEDEV';
6495
                        break;
6496
                    case 270:
6497
                        $function = 'BETADIST';
6498
                        break;
6499
                    case 272:
6500
                        $function = 'BETAINV';
6501
                        break;
6502
                    case 317:
6503
                        $function = 'PROB';
6504
                        break;
6505
                    case 318:
6506
                        $function = 'DEVSQ';
6507
                        break;
6508
                    case 319:
6509
                        $function = 'GEOMEAN';
6510
                        break;
6511
                    case 320:
6512
                        $function = 'HARMEAN';
6513
                        break;
6514
                    case 321:
6515
                        $function = 'SUMSQ';
6516
                        break;
6517
                    case 322:
6518
                        $function = 'KURT';
6519
                        break;
6520
                    case 323:
6521
                        $function = 'SKEW';
6522
                        break;
6523
                    case 324:
6524
                        $function = 'ZTEST';
6525
                        break;
6526
                    case 329:
6527
                        $function = 'PERCENTRANK';
6528
                        break;
6529
                    case 330:
6530
                        $function = 'MODE';
6531
                        break;
6532
                    case 336:
6533
                        $function = 'CONCATENATE';
6534
                        break;
6535
                    case 344:
6536
                        $function = 'SUBTOTAL';
6537
                        break;
6538
                    case 345:
6539
                        $function = 'SUMIF';
6540
                        break;
6541
                    case 354:
6542
                        $function = 'ROMAN';
6543
                        break;
6544
                    case 358:
6545
                        $function = 'GETPIVOTDATA';
6546
                        break;
6547
                    case 359:
6548
                        $function = 'HYPERLINK';
6549
                        break;
6550
                    case 361:
6551
                        $function = 'AVERAGEA';
6552
                        break;
6553
                    case 362:
6554
                        $function = 'MAXA';
6555
                        break;
6556
                    case 363:
6557
                        $function = 'MINA';
6558
                        break;
6559
                    case 364:
6560
                        $function = 'STDEVPA';
6561
                        break;
6562
                    case 365:
6563
                        $function = 'VARPA';
6564
                        break;
6565
                    case 366:
6566
                        $function = 'STDEVA';
6567
                        break;
6568
                    case 367:
6569
                        $function = 'VARA';
6570
                        break;
6571
                    default:
6572
                        throw new Exception('Unrecognized function in formula');
6573
                        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...
6574
                }
6575
                $data = ['function' => $function, 'args' => $args];
6576
                break;
6577
            case 0x23:    //    index to defined name
6578
            case 0x43:
6579
            case 0x63:
6580
                $name = 'tName';
6581
                $size = 5;
6582
                // offset: 1; size: 2; one-based index to definedname record
6583
                $definedNameIndex = self::getInt2d($formulaData, 1) - 1;
6584
                // offset: 2; size: 2; not used
6585
                $data = $this->definedname[$definedNameIndex]['name'];
6586
                break;
6587
            case 0x24:    //    single cell reference e.g. A5
6588
            case 0x44:
6589
            case 0x64:
6590
                $name = 'tRef';
6591
                $size = 5;
6592
                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
6593
                break;
6594
            case 0x25:    //    cell range reference to cells in the same sheet (2d)
6595
            case 0x45:
6596
            case 0x65:
6597
                $name = 'tArea';
6598
                $size = 9;
6599
                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
6600
                break;
6601
            case 0x26:    //    Constant reference sub-expression
6602
            case 0x46:
6603 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...
6604
                $name = 'tMemArea';
6605
                // offset: 1; size: 4; not used
6606
                // offset: 5; size: 2; size of the following subexpression
6607
                $subSize = self::getInt2d($formulaData, 5);
6608
                $size = 7 + $subSize;
6609
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6610
                break;
6611
            case 0x27:    //    Deleted constant reference sub-expression
6612
            case 0x47:
6613 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...
6614
                $name = 'tMemErr';
6615
                // offset: 1; size: 4; not used
6616
                // offset: 5; size: 2; size of the following subexpression
6617
                $subSize = self::getInt2d($formulaData, 5);
6618
                $size = 7 + $subSize;
6619
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6620
                break;
6621
            case 0x29:    //    Variable reference sub-expression
6622
            case 0x49:
6623 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...
6624
                $name = 'tMemFunc';
6625
                // offset: 1; size: 2; size of the following sub-expression
6626
                $subSize = self::getInt2d($formulaData, 1);
6627
                $size = 3 + $subSize;
6628
                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
6629
                break;
6630
            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
6631
            case 0x4C:
6632
            case 0x6C:
6633
                $name = 'tRefN';
6634
                $size = 5;
6635
                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
6636
                break;
6637
            case 0x2D:    //    Relative 2d range reference
6638
            case 0x4D:
6639
            case 0x6D:
6640
                $name = 'tAreaN';
6641
                $size = 9;
6642
                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
6643
                break;
6644
            case 0x39:    //    External name
6645
            case 0x59:
6646
            case 0x79:
6647
                $name = 'tNameX';
6648
                $size = 7;
6649
                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
6650
                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
6651
                $index = self::getInt2d($formulaData, 3);
6652
                // assume index is to EXTERNNAME record
6653
                $data = $this->externalNames[$index - 1]['name'];
6654
                // offset: 5; size: 2; not used
6655
                break;
6656
            case 0x3A:    //    3d reference to cell
6657
            case 0x5A:
6658 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...
6659
                $name = 'tRef3d';
6660
                $size = 7;
6661
6662
                try {
6663
                    // offset: 1; size: 2; index to REF entry
6664
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6665
                    // offset: 3; size: 4; cell address
6666
                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
6667
6668
                    $data = "$sheetRange!$cellAddress";
6669
                } catch (\PhpSpreadsheet\Exception $e) {
6670
                    // deleted sheet reference
6671
                    $data = '#REF!';
6672
                }
6673
                break;
6674
            case 0x3B:    //    3d reference to cell range
6675
            case 0x5B:
6676 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...
6677
                $name = 'tArea3d';
6678
                $size = 11;
6679
6680
                try {
6681
                    // offset: 1; size: 2; index to REF entry
6682
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6683
                    // offset: 3; size: 8; cell address
6684
                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
6685
6686
                    $data = "$sheetRange!$cellRangeAddress";
6687
                } catch (\PhpSpreadsheet\Exception $e) {
6688
                    // deleted sheet reference
6689
                    $data = '#REF!';
6690
                }
6691
                break;
6692
            // Unknown cases    // don't know how to deal with
6693
            default:
6694
                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
6695
                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...
6696
        }
6697
6698
        return [
6699
            'id' => $id,
6700
            'name' => $name,
6701
            'size' => $size,
6702
            'data' => $data,
6703
        ];
6704
    }
6705
6706
    /**
6707
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
6708
     * section 3.3.4
6709
     *
6710
     * @param string $cellAddressStructure
6711
     * @return string
6712
     */
6713
    private function readBIFF8CellAddress($cellAddressStructure)
6714
    {
6715
        // 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...
6716
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6717
6718
        // offset: 2; size: 2; index to column or column offset + relative flags
6719
        // bit: 7-0; mask 0x00FF; column index
6720
        $column = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($cellAddressStructure, 2));
6721
6722
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6723
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
6724
            $column = '$' . $column;
6725
        }
6726
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6727
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6728
            $row = '$' . $row;
6729
        }
6730
6731
        return $column . $row;
6732
    }
6733
6734
    /**
6735
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6736
     * to indicate offsets from a base cell
6737
     * section 3.3.4
6738
     *
6739
     * @param string $cellAddressStructure
6740
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
6741
     * @return string
6742
     */
6743
    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
6744
    {
6745
        list($baseCol, $baseRow) = \PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6746
        $baseCol = \PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6747
6748
        // 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...
6749
        $rowIndex = self::getInt2d($cellAddressStructure, 0);
6750
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6751
6752
        // offset: 2; size: 2; index to column or column offset + relative flags
6753
        // bit: 7-0; mask 0x00FF; column index
6754
        $colIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
6755
6756
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6757 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...
6758
            $column = \PhpSpreadsheet\Cell::stringFromColumnIndex($colIndex);
6759
            $column = '$' . $column;
6760
        } else {
6761
            $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
6762
            $column = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $colIndex);
6763
        }
6764
6765
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6766
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6767
            $row = '$' . $row;
6768
        } else {
6769
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
6770
            $row = $baseRow + $rowIndex;
6771
        }
6772
6773
        return $column . $row;
6774
    }
6775
6776
    /**
6777
     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
6778
     * always fixed range
6779
     * section 2.5.14
6780
     *
6781
     * @param string $subData
6782
     * @throws Exception
6783
     * @return string
6784
     */
6785
    private function readBIFF5CellRangeAddressFixed($subData)
6786
    {
6787
        // offset: 0; size: 2; index to first row
6788
        $fr = self::getInt2d($subData, 0) + 1;
6789
6790
        // offset: 2; size: 2; index to last row
6791
        $lr = self::getInt2d($subData, 2) + 1;
6792
6793
        // offset: 4; size: 1; index to first column
6794
        $fc = ord($subData{4});
6795
6796
        // offset: 5; size: 1; index to last column
6797
        $lc = ord($subData{5});
6798
6799
        // check values
6800
        if ($fr > $lr || $fc > $lc) {
6801
            throw new Exception('Not a cell range address');
6802
        }
6803
6804
        // column index to letter
6805
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6806
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6807
6808
        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...
6809
            return "$fc$fr";
6810
        }
6811
6812
        return "$fc$fr:$lc$lr";
6813
    }
6814
6815
    /**
6816
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
6817
     * always fixed range
6818
     * section 2.5.14
6819
     *
6820
     * @param string $subData
6821
     * @throws Exception
6822
     * @return string
6823
     */
6824
    private function readBIFF8CellRangeAddressFixed($subData)
6825
    {
6826
        // offset: 0; size: 2; index to first row
6827
        $fr = self::getInt2d($subData, 0) + 1;
6828
6829
        // offset: 2; size: 2; index to last row
6830
        $lr = self::getInt2d($subData, 2) + 1;
6831
6832
        // offset: 4; size: 2; index to first column
6833
        $fc = self::getInt2d($subData, 4);
6834
6835
        // offset: 6; size: 2; index to last column
6836
        $lc = self::getInt2d($subData, 6);
6837
6838
        // check values
6839
        if ($fr > $lr || $fc > $lc) {
6840
            throw new Exception('Not a cell range address');
6841
        }
6842
6843
        // column index to letter
6844
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6845
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6846
6847
        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...
6848
            return "$fc$fr";
6849
        }
6850
6851
        return "$fc$fr:$lc$lr";
6852
    }
6853
6854
    /**
6855
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
6856
     * there are flags indicating whether column/row index is relative
6857
     * section 3.3.4
6858
     *
6859
     * @param string $subData
6860
     * @return string
6861
     */
6862
    private function readBIFF8CellRangeAddress($subData)
6863
    {
6864
        // todo: if cell range is just a single cell, should this funciton
6865
        // not just return e.g. 'A1' and not 'A1:A1' ?
6866
6867
        // 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...
6868
            $fr = self::getInt2d($subData, 0) + 1;
6869
6870
        // 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...
6871
            $lr = self::getInt2d($subData, 2) + 1;
6872
6873
        // offset: 4; size: 2; index to first column or column offset + relative flags
6874
6875
        // bit: 7-0; mask 0x00FF; column index
6876
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 4));
6877
6878
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6879
        if (!(0x4000 & self::getInt2d($subData, 4))) {
6880
            $fc = '$' . $fc;
6881
        }
6882
6883
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6884
        if (!(0x8000 & self::getInt2d($subData, 4))) {
6885
            $fr = '$' . $fr;
6886
        }
6887
6888
        // offset: 6; size: 2; index to last column or column offset + relative flags
6889
6890
        // bit: 7-0; mask 0x00FF; column index
6891
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 6));
6892
6893
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6894
        if (!(0x4000 & self::getInt2d($subData, 6))) {
6895
            $lc = '$' . $lc;
6896
        }
6897
6898
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6899
        if (!(0x8000 & self::getInt2d($subData, 6))) {
6900
            $lr = '$' . $lr;
6901
        }
6902
6903
        return "$fc$fr:$lc$lr";
6904
    }
6905
6906
    /**
6907
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6908
     * to indicate offsets from a base cell
6909
     * section 3.3.4
6910
     *
6911
     * @param string $subData
6912
     * @param string $baseCell Base cell
6913
     * @return string Cell range address
6914
     */
6915
    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
6916
    {
6917
        list($baseCol, $baseRow) = \PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6918
        $baseCol = \PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6919
6920
        // TODO: if cell range is just a single cell, should this funciton
6921
        // not just return e.g. 'A1' and not 'A1:A1' ?
6922
6923
        // offset: 0; size: 2; first row
6924
        $frIndex = self::getInt2d($subData, 0); // adjust below
6925
6926
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
6927
        $lrIndex = self::getInt2d($subData, 2); // adjust below
6928
6929
        // offset: 4; size: 2; first column with relative/absolute flags
6930
6931
        // bit: 7-0; mask 0x00FF; column index
6932
        $fcIndex = 0x00FF & self::getInt2d($subData, 4);
6933
6934
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6935 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...
6936
            // absolute column index
6937
            $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fcIndex);
6938
            $fc = '$' . $fc;
6939
        } else {
6940
            // column offset
6941
            $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
6942
            $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $fcIndex);
6943
        }
6944
6945
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6946 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...
6947
            // absolute row index
6948
            $fr = $frIndex + 1;
6949
            $fr = '$' . $fr;
6950
        } else {
6951
            // row offset
6952
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
6953
            $fr = $baseRow + $frIndex;
6954
        }
6955
6956
        // offset: 6; size: 2; last column with relative/absolute flags
6957
6958
        // bit: 7-0; mask 0x00FF; column index
6959
        $lcIndex = 0x00FF & self::getInt2d($subData, 6);
6960
        $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6961
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
0 ignored issues
show
Unused Code introduced by
$lc is not used, you could remove the assignment.

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

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

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

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

Loading history...
6962
6963
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6964 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...
6965
            // absolute column index
6966
            $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lcIndex);
6967
            $lc = '$' . $lc;
6968
        } else {
6969
            // column offset
6970
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6971
            $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
6972
        }
6973
6974
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6975 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...
6976
            // absolute row index
6977
            $lr = $lrIndex + 1;
6978
            $lr = '$' . $lr;
6979
        } else {
6980
            // row offset
6981
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
6982
            $lr = $baseRow + $lrIndex;
6983
        }
6984
6985
        return "$fc$fr:$lc$lr";
6986
    }
6987
6988
    /**
6989
     * Read BIFF8 cell range address list
6990
     * section 2.5.15
6991
     *
6992
     * @param string $subData
6993
     * @return array
6994
     */
6995 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...
6996
    {
6997
        $cellRangeAddresses = [];
6998
6999
        // offset: 0; size: 2; number of the following cell range addresses
7000
        $nm = self::getInt2d($subData, 0);
7001
7002
        $offset = 2;
7003
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
7004
        for ($i = 0; $i < $nm; ++$i) {
7005
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
7006
            $offset += 8;
7007
        }
7008
7009
        return [
7010
            'size' => 2 + 8 * $nm,
7011
            'cellRangeAddresses' => $cellRangeAddresses,
7012
        ];
7013
    }
7014
7015
    /**
7016
     * Read BIFF5 cell range address list
7017
     * section 2.5.15
7018
     *
7019
     * @param string $subData
7020
     * @return array
7021
     */
7022 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...
7023
    {
7024
        $cellRangeAddresses = [];
7025
7026
        // offset: 0; size: 2; number of the following cell range addresses
7027
        $nm = self::getInt2d($subData, 0);
7028
7029
        $offset = 2;
7030
        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
7031
        for ($i = 0; $i < $nm; ++$i) {
7032
            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
7033
            $offset += 6;
7034
        }
7035
7036
        return [
7037
            'size' => 2 + 6 * $nm,
7038
            'cellRangeAddresses' => $cellRangeAddresses,
7039
        ];
7040
    }
7041
7042
    /**
7043
     * Get a sheet range like Sheet1:Sheet3 from REF index
7044
     * Note: If there is only one sheet in the range, one gets e.g Sheet1
7045
     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
7046
     * in which case an Exception is thrown
7047
     *
7048
     * @param int $index
7049
     * @throws Exception
7050
     * @return string|false
7051
     */
7052
    private function readSheetRangeByRefIndex($index)
7053
    {
7054
        if (isset($this->ref[$index])) {
7055
            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
7056
7057
            switch ($type) {
7058
                case 'internal':
7059
                    // check if we have a deleted 3d reference
7060
                    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...
7061
                        throw new Exception('Deleted sheet reference');
7062
                    }
7063
7064
                    // we have normal sheet range (collapsed or uncollapsed)
7065
                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
7066
                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
7067
7068
                    if ($firstSheetName == $lastSheetName) {
7069
                        // collapsed sheet range
7070
                        $sheetRange = $firstSheetName;
7071
                    } else {
7072
                        $sheetRange = "$firstSheetName:$lastSheetName";
7073
                    }
7074
7075
                    // escape the single-quotes
7076
                    $sheetRange = str_replace("'", "''", $sheetRange);
7077
7078
                    // if there are special characters, we need to enclose the range in single-quotes
7079
                    // todo: check if we have identified the whole set of special characters
7080
                    // it seems that the following characters are not accepted for sheet names
7081
                    // and we may assume that they are not present: []*/:\?
7082
                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
7083
                        $sheetRange = "'$sheetRange'";
7084
                    }
7085
7086
                    return $sheetRange;
7087
                    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...
7088
                default:
7089
                    // TODO: external sheet support
7090
                    throw new Exception('Excel5 reader only supports internal sheets in fomulas');
7091
                    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...
7092
            }
7093
        }
7094
7095
        return false;
7096
    }
7097
7098
    /**
7099
     * read BIFF8 constant value array from array data
7100
     * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
7101
     * section 2.5.8
7102
     *
7103
     * @param string $arrayData
7104
     * @return array
7105
     */
7106
    private static function readBIFF8ConstantArray($arrayData)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
7107
    {
7108
        // offset: 0; size: 1; number of columns decreased by 1
7109
        $nc = ord($arrayData[0]);
7110
7111
        // offset: 1; size: 2; number of rows decreased by 1
7112
        $nr = self::getInt2d($arrayData, 1);
7113
        $size = 3; // initialize
7114
        $arrayData = substr($arrayData, 3);
7115
7116
        // 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...
7117
        $matrixChunks = [];
7118
        for ($r = 1; $r <= $nr + 1; ++$r) {
7119
            $items = [];
7120
            for ($c = 1; $c <= $nc + 1; ++$c) {
7121
                $constant = self::_readBIFF8Constant($arrayData);
0 ignored issues
show
Bug introduced by
The method _readBIFF8Constant() does not exist on PhpSpreadsheet\Reader\Excel5. Did you maybe mean readBIFF8Constant()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
7122
                $items[] = $constant['value'];
7123
                $arrayData = substr($arrayData, $constant['size']);
7124
                $size += $constant['size'];
7125
            }
7126
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
7127
        }
7128
        $matrix = '{' . implode(';', $matrixChunks) . '}';
7129
7130
        return [
7131
            'value' => $matrix,
7132
            'size' => $size,
7133
        ];
7134
    }
7135
7136
    /**
7137
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
7138
     * section 2.5.7
7139
     * returns e.g. array('value' => '5', 'size' => 9)
7140
     *
7141
     * @param string $valueData
7142
     * @return array
7143
     */
7144
    private static function readBIFF8Constant($valueData)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
7145
    {
7146
        // offset: 0; size: 1; identifier for type of constant
7147
        $identifier = ord($valueData[0]);
7148
7149
        switch ($identifier) {
7150
            case 0x00: // empty constant (what is this?)
7151
                $value = '';
7152
                $size = 9;
7153
                break;
7154
            case 0x01: // number
7155
                // offset: 1; size: 8; IEEE 754 floating-point value
7156
                $value = self::extractNumber(substr($valueData, 1, 8));
7157
                $size = 9;
7158
                break;
7159
            case 0x02: // string value
7160
                // offset: 1; size: var; Unicode string, 16-bit string length
7161
                $string = self::readUnicodeStringLong(substr($valueData, 1));
7162
                $value = '"' . $string['value'] . '"';
7163
                $size = 1 + $string['size'];
7164
                break;
7165
            case 0x04: // boolean
7166
                // 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...
7167
                if (ord($valueData[1])) {
7168
                    $value = 'TRUE';
7169
                } else {
7170
                    $value = 'FALSE';
7171
                }
7172
                $size = 9;
7173
                break;
7174
            case 0x10: // error code
7175
                // offset: 1; size: 1; error code
7176
                $value = Excel5\ErrorCode::lookup(ord($valueData[1]));
7177
                $size = 9;
7178
                break;
7179
        }
7180
7181
        return [
7182
            '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...
7183
            '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...
7184
        ];
7185
    }
7186
7187
    /**
7188
     * Extract RGB color
7189
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
7190
     *
7191
     * @param string $rgb Encoded RGB value (4 bytes)
7192
     * @return array
7193
     */
7194
    private static function readRGB($rgb)
7195
    {
7196
        // offset: 0; size 1; Red component
7197
        $r = ord($rgb{0});
7198
7199
        // offset: 1; size: 1; Green component
7200
        $g = ord($rgb{1});
7201
7202
        // offset: 2; size: 1; Blue component
7203
        $b = ord($rgb{2});
7204
7205
        // HEX notation, e.g. 'FF00FC'
7206
        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
7207
7208
        return ['rgb' => $rgb];
7209
    }
7210
7211
    /**
7212
     * Read byte string (8-bit string length)
7213
     * OpenOffice documentation: 2.5.2
7214
     *
7215
     * @param string $subData
7216
     * @return array
7217
     */
7218 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...
7219
    {
7220
        // offset: 0; size: 1; length of the string (character count)
7221
        $ln = ord($subData[0]);
7222
7223
        // 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...
7224
        $value = $this->decodeCodepage(substr($subData, 1, $ln));
7225
7226
        return [
7227
            'value' => $value,
7228
            'size' => 1 + $ln, // size in bytes of data structure
7229
        ];
7230
    }
7231
7232
    /**
7233
     * Read byte string (16-bit string length)
7234
     * OpenOffice documentation: 2.5.2
7235
     *
7236
     * @param string $subData
7237
     * @return array
7238
     */
7239 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...
7240
    {
7241
        // offset: 0; size: 2; length of the string (character count)
7242
        $ln = self::getInt2d($subData, 0);
7243
7244
        // 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...
7245
        $value = $this->decodeCodepage(substr($subData, 2));
7246
7247
        //return $string;
7248
        return [
7249
            'value' => $value,
7250
            'size' => 2 + $ln, // size in bytes of data structure
7251
        ];
7252
    }
7253
7254
    /**
7255
     * Extracts an Excel Unicode short string (8-bit string length)
7256
     * OpenOffice documentation: 2.5.3
7257
     * function will automatically find out where the Unicode string ends.
7258
     *
7259
     * @param string $subData
7260
     * @return array
7261
     */
7262 View Code Duplication
    private static function readUnicodeStringShort($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

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

Loading history...
7265
7266
        // offset: 0: size: 1; length of the string (character count)
7267
        $characterCount = ord($subData[0]);
7268
7269
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7270
7271
        // add 1 for the string length
7272
        $string['size'] += 1;
7273
7274
        return $string;
7275
    }
7276
7277
    /**
7278
     * Extracts an Excel Unicode long string (16-bit string length)
7279
     * OpenOffice documentation: 2.5.3
7280
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7281
     *
7282
     * @param string $subData
7283
     * @return array
7284
     */
7285 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...
7286
    {
7287
        $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...
7288
7289
        // offset: 0: size: 2; length of the string (character count)
7290
        $characterCount = self::getInt2d($subData, 0);
7291
7292
        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7293
7294
        // add 2 for the string length
7295
        $string['size'] += 2;
7296
7297
        return $string;
7298
    }
7299
7300
    /**
7301
     * Read Unicode string with no string length field, but with known character count
7302
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7303
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
7304
     *
7305
     * @param string $subData
7306
     * @param int $characterCount
7307
     * @return array
7308
     */
7309
    private static function readUnicodeString($subData, $characterCount)
7310
    {
7311
        $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...
7312
7313
        // offset: 0: size: 1; option flags
7314
        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
7315
        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7316
7317
        // bit: 2; mask: 0x04; Asian phonetic settings
7318
        $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...
7319
7320
        // bit: 3; mask: 0x08; Rich-Text settings
7321
        $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...
7322
7323
        // 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...
7324
        // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
7325
        // needs to be fixed
7326
        $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
7327
7328
        return [
7329
            'value' => $value,
7330
            'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
7331
        ];
7332
    }
7333
7334
    /**
7335
     * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
7336
     * Example:  hello"world  -->  "hello""world"
7337
     *
7338
     * @param string $value UTF-8 encoded string
7339
     * @return string
7340
     */
7341
    private static function UTF8toExcelDoubleQuoted($value)
7342
    {
7343
        return '"' . str_replace('"', '""', $value) . '"';
7344
    }
7345
7346
    /**
7347
     * Reads first 8 bytes of a string and return IEEE 754 float
7348
     *
7349
     * @param string $data Binary string that is at least 8 bytes long
7350
     * @return float
7351
     */
7352
    private static function extractNumber($data)
7353
    {
7354
        $rknumhigh = self::getInt4d($data, 4);
7355
        $rknumlow = self::getInt4d($data, 0);
7356
        $sign = ($rknumhigh & 0x80000000) >> 31;
7357
        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
7358
        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
7359
        $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
7360
        $mantissalow2 = ($rknumlow & 0x7fffffff);
7361
        $value = $mantissa / pow(2, (20 - $exp));
7362
7363
        if ($mantissalow1 != 0) {
7364
            $value += 1 / pow(2, (21 - $exp));
7365
        }
7366
7367
        $value += $mantissalow2 / pow(2, (52 - $exp));
7368
        if ($sign) {
7369
            $value *= -1;
7370
        }
7371
7372
        return $value;
7373
    }
7374
7375
    private static function getIEEE754($rknum)
7376
    {
7377
        if (($rknum & 0x02) != 0) {
7378
            $value = $rknum >> 2;
7379
        } else {
7380
            // changes by mmp, info on IEEE754 encoding from
7381
            // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
7382
            // The RK format calls for using only the most significant 30 bits
7383
            // of the 64 bit floating point value. The other 34 bits are assumed
7384
            // to be 0 so we use the upper 30 bits of $rknum as follows...
7385
            $sign = ($rknum & 0x80000000) >> 31;
7386
            $exp = ($rknum & 0x7ff00000) >> 20;
7387
            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
7388
            $value = $mantissa / pow(2, (20 - ($exp - 1023)));
7389
            if ($sign) {
7390
                $value = -1 * $value;
7391
            }
7392
            //end of changes by mmp
7393
        }
7394
        if (($rknum & 0x01) != 0) {
7395
            $value /= 100;
7396
        }
7397
7398
        return $value;
7399
    }
7400
7401
    /**
7402
     * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
7403
     *
7404
     * @param string $string
7405
     * @param bool $compressed
7406
     * @return string
7407
     */
7408
    private static function encodeUTF16($string, $compressed = '')
7409
    {
7410
        if ($compressed) {
7411
            $string = self::uncompressByteString($string);
7412
        }
7413
7414
        return \PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
7415
    }
7416
7417
    /**
7418
     * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
7419
     *
7420
     * @param string $string
7421
     * @return string
7422
     */
7423
    private static function uncompressByteString($string)
7424
    {
7425
        $uncompressedString = '';
7426
        $strLen = strlen($string);
7427
        for ($i = 0; $i < $strLen; ++$i) {
7428
            $uncompressedString .= $string[$i] . "\0";
7429
        }
7430
7431
        return $uncompressedString;
7432
    }
7433
7434
    /**
7435
     * Convert string to UTF-8. Only used for BIFF5.
7436
     *
7437
     * @param string $string
7438
     * @return string
7439
     */
7440
    private function decodeCodepage($string)
7441
    {
7442
        return \PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
7443
    }
7444
7445
    /**
7446
     * Read 16-bit unsigned integer
7447
     *
7448
     * @param string $data
7449
     * @param int $pos
7450
     * @return int
7451
     */
7452
    public static function getInt2d($data, $pos)
7453
    {
7454
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
7455
    }
7456
7457
    /**
7458
     * Read 32-bit signed integer
7459
     *
7460
     * @param string $data
7461
     * @param int $pos
7462
     * @return int
7463
     */
7464 View Code Duplication
    public static function getInt4d($data, $pos)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
7465
    {
7466
        // FIX: represent numbers correctly on 64-bit system
7467
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
7468
        // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
7469
        $_or_24 = ord($data[$pos + 3]);
7470
        if ($_or_24 >= 128) {
7471
            // negative number
7472
            $_ord_24 = -abs((256 - $_or_24) << 24);
7473
        } else {
7474
            $_ord_24 = ($_or_24 & 127) << 24;
7475
        }
7476
7477
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
7478
    }
7479
7480
    private function parseRichText($is = '')
7481
    {
7482
        $value = new \PhpSpreadsheet\RichText();
7483
        $value->createText($is);
7484
7485
        return $value;
7486
    }
7487
}
7488