Completed
Push — develop ( e1f81f...539a89 )
by Adrien
16:11
created

Excel5::readLabelSst()   C

Complexity

Conditions 17
Paths 9

Size

Total Lines 75
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 43
nc 9
nop 0
dl 0
loc 75
rs 5.2934
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace 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 integer
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 boolean
288
     */
289
    private $frozen;
290
291
    /**
292
     * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
293
     *
294
     * @var boolean
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
     * @return     boolean
417
     * @throws Exception
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
            return true;
433
        } catch (\PhpSpreadsheet\Exception $e) {
434
            return false;
435
        }
436
    }
437
438
    /**
439
     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object
440
     *
441
     * @param     string         $pFilename
442
     * @throws     Exception
443
     */
444
    public function listWorksheetNames($pFilename)
445
    {
446
        // Check if file exists
447
        if (!file_exists($pFilename)) {
448
            throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
449
        }
450
451
        $worksheetNames = array();
452
453
        // Read the OLE file
454
        $this->loadOLE($pFilename);
455
456
        // total byte size of Excel data (workbook global substream + sheet substreams)
457
        $this->dataSize = strlen($this->data);
458
459
        $this->pos        = 0;
460
        $this->sheets    = array();
461
462
        // Parse Workbook Global Substream
463 View Code Duplication
        while ($this->pos < $this->dataSize) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

Loading history...
485
                continue;
486
            }
487
488
            $worksheetNames[] = $sheet['name'];
489
        }
490
491
        return $worksheetNames;
492
    }
493
494
495
    /**
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 = array();
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 = array();
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 = array();
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
    /**
603
     * Loads PhpSpreadsheet from file
604
     *
605
     * @param     string         $pFilename
606
     * @return    \PhpSpreadsheet\Spreadsheet
607
     * @throws    Exception
608
     */
609
    public function load($pFilename)
610
    {
611
        // Read the OLE file
612
        $this->loadOLE($pFilename);
613
614
        // Initialisations
615
        $this->spreadsheet = new \PhpSpreadsheet\Spreadsheet();
616
        $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
617
        if (!$this->readDataOnly) {
618
            $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
619
            $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
620
        }
621
622
        // Read the summary information stream (containing meta data)
623
        $this->readSummaryInformation();
624
625
        // Read the Additional document summary information stream (containing application-specific meta data)
626
        $this->readDocumentSummaryInformation();
627
628
        // total byte size of Excel data (workbook global substream + sheet substreams)
629
        $this->dataSize = strlen($this->data);
630
631
        // initialize
632
        $this->pos                 = 0;
633
        $this->codepage            = 'CP1252';
634
        $this->formats             = array();
635
        $this->objFonts            = array();
636
        $this->palette             = array();
637
        $this->sheets              = array();
638
        $this->externalBooks       = array();
639
        $this->ref                 = array();
640
        $this->definedname         = array();
641
        $this->sst                 = array();
642
        $this->drawingGroupData    = '';
643
        $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...
644
        $this->mapCellXfIndex      = array();
645
        $this->mapCellStyleXfIndex = array();
646
647
        // Parse Workbook Global Substream
648
        while ($this->pos < $this->dataSize) {
649
            $code = self::getInt2d($this->data, $this->pos);
650
651
            switch ($code) {
652
                case self::XLS_TYPE_BOF:
653
                    $this->readBof();
654
                    break;
655
                case self::XLS_TYPE_FILEPASS:
656
                    $this->readFilepass();
657
                    break;
658
                case self::XLS_TYPE_CODEPAGE:
659
                    $this->readCodepage();
660
                    break;
661
                case self::XLS_TYPE_DATEMODE:
662
                    $this->readDateMode();
663
                    break;
664
                case self::XLS_TYPE_FONT:
665
                    $this->readFont();
666
                    break;
667
                case self::XLS_TYPE_FORMAT:
668
                    $this->readFormat();
669
                    break;
670
                case self::XLS_TYPE_XF:
671
                    $this->readXf();
672
                    break;
673
                case self::XLS_TYPE_XFEXT:
674
                    $this->readXfExt();
675
                    break;
676
                case self::XLS_TYPE_STYLE:
677
                    $this->readStyle();
678
                    break;
679
                case self::XLS_TYPE_PALETTE:
680
                    $this->readPalette();
681
                    break;
682
                case self::XLS_TYPE_SHEET:
683
                    $this->readSheet();
684
                    break;
685
                case self::XLS_TYPE_EXTERNALBOOK:
686
                    $this->readExternalBook();
687
                    break;
688
                case self::XLS_TYPE_EXTERNNAME:
689
                    $this->readExternName();
690
                    break;
691
                case self::XLS_TYPE_EXTERNSHEET:
692
                    $this->readExternSheet();
693
                    break;
694
                case self::XLS_TYPE_DEFINEDNAME:
695
                    $this->readDefinedName();
696
                    break;
697
                case self::XLS_TYPE_MSODRAWINGGROUP:
698
                    $this->readMsoDrawingGroup();
699
                    break;
700
                case self::XLS_TYPE_SST:
701
                    $this->readSst();
702
                    break;
703
                case self::XLS_TYPE_EOF:
704
                    $this->readDefault();
705
                    break 2;
706
                default:
707
                    $this->readDefault();
708
                    break;
709
            }
710
        }
711
712
        // Resolve indexed colors for font, fill, and border colors
713
        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
714
        if (!$this->readDataOnly) {
715
            foreach ($this->objFonts as $objFont) {
716 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...
717
                    $color = Excel5\Color::map($objFont->colorIndex, $this->palette, $this->version);
718
                    $objFont->getColor()->setRGB($color['rgb']);
719
                }
720
            }
721
722
            foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
723
                // fill start and end color
724
                $fill = $objStyle->getFill();
725
726
                if (isset($fill->startcolorIndex)) {
727
                    $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...
728
                    $fill->getStartColor()->setRGB($startColor['rgb']);
729
                }
730
                if (isset($fill->endcolorIndex)) {
731
                    $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...
732
                    $fill->getEndColor()->setRGB($endColor['rgb']);
733
                }
734
735
                // border colors
736
                $top      = $objStyle->getBorders()->getTop();
737
                $right    = $objStyle->getBorders()->getRight();
738
                $bottom   = $objStyle->getBorders()->getBottom();
739
                $left     = $objStyle->getBorders()->getLeft();
740
                $diagonal = $objStyle->getBorders()->getDiagonal();
741
742 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...
743
                    $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...
744
                    $top->getColor()->setRGB($borderTopColor['rgb']);
745
                }
746 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...
747
                    $borderRightColor = Excel5\Color::map($right->colorIndex, $this->palette, $this->version);
748
                    $right->getColor()->setRGB($borderRightColor['rgb']);
749
                }
750 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...
751
                    $borderBottomColor = Excel5\Color::map($bottom->colorIndex, $this->palette, $this->version);
752
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
753
                }
754 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...
755
                    $borderLeftColor = Excel5\Color::map($left->colorIndex, $this->palette, $this->version);
756
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
757
                }
758 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...
759
                    $borderDiagonalColor = Excel5\Color::map($diagonal->colorIndex, $this->palette, $this->version);
760
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
761
                }
762
            }
763
        }
764
765
        // treat MSODRAWINGGROUP records, workbook-level Escher
766
        if (!$this->readDataOnly && $this->drawingGroupData) {
767
            $escherWorkbook = new \PhpSpreadsheet\Shared\Escher();
768
            $reader = new Excel5\Escher($escherWorkbook);
769
            $escherWorkbook = $reader->load($this->drawingGroupData);
770
        }
771
772
        // Parse the individual sheets
773
        foreach ($this->sheets as $sheet) {
774
            if ($sheet['sheetType'] != 0x00) {
775
                // 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...
776
                continue;
777
            }
778
779
            // check if sheet should be skipped
780
            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
781
                continue;
782
            }
783
784
            // add sheet to PhpSpreadsheet object
785
            $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...
786
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
787
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
788
            //        name in line with the formula, not the reverse
789
            $this->phpSheet->setTitle($sheet['name'], false);
790
            $this->phpSheet->setSheetState($sheet['sheetState']);
791
792
            $this->pos = $sheet['offset'];
793
794
            // Initialize isFitToPages. May change after reading SHEETPR record.
795
            $this->isFitToPages = false;
796
797
            // Initialize drawingData
798
            $this->drawingData = '';
799
800
            // Initialize objs
801
            $this->objs = array();
802
803
            // Initialize shared formula parts
804
            $this->sharedFormulaParts = array();
805
806
            // Initialize shared formulas
807
            $this->sharedFormulas = array();
808
809
            // Initialize text objs
810
            $this->textObjects = array();
811
812
            // Initialize cell annotations
813
            $this->cellNotes = array();
814
            $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...
815
816
            while ($this->pos <= $this->dataSize - 4) {
817
                $code = self::getInt2d($this->data, $this->pos);
818
819
                switch ($code) {
820
                    case self::XLS_TYPE_BOF:
821
                        $this->readBof();
822
                        break;
823
                    case self::XLS_TYPE_PRINTGRIDLINES:
824
                        $this->readPrintGridlines();
825
                        break;
826
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
827
                        $this->readDefaultRowHeight();
828
                        break;
829
                    case self::XLS_TYPE_SHEETPR:
830
                        $this->readSheetPr();
831
                        break;
832
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
833
                        $this->readHorizontalPageBreaks();
834
                        break;
835
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
836
                        $this->readVerticalPageBreaks();
837
                        break;
838
                    case self::XLS_TYPE_HEADER:
839
                        $this->readHeader();
840
                        break;
841
                    case self::XLS_TYPE_FOOTER:
842
                        $this->readFooter();
843
                        break;
844
                    case self::XLS_TYPE_HCENTER:
845
                        $this->readHcenter();
846
                        break;
847
                    case self::XLS_TYPE_VCENTER:
848
                        $this->readVcenter();
849
                        break;
850
                    case self::XLS_TYPE_LEFTMARGIN:
851
                        $this->readLeftMargin();
852
                        break;
853
                    case self::XLS_TYPE_RIGHTMARGIN:
854
                        $this->readRightMargin();
855
                        break;
856
                    case self::XLS_TYPE_TOPMARGIN:
857
                        $this->readTopMargin();
858
                        break;
859
                    case self::XLS_TYPE_BOTTOMMARGIN:
860
                        $this->readBottomMargin();
861
                        break;
862
                    case self::XLS_TYPE_PAGESETUP:
863
                        $this->readPageSetup();
864
                        break;
865
                    case self::XLS_TYPE_PROTECT:
866
                        $this->readProtect();
867
                        break;
868
                    case self::XLS_TYPE_SCENPROTECT:
869
                        $this->readScenProtect();
870
                        break;
871
                    case self::XLS_TYPE_OBJECTPROTECT:
872
                        $this->readObjectProtect();
873
                        break;
874
                    case self::XLS_TYPE_PASSWORD:
875
                        $this->readPassword();
876
                        break;
877
                    case self::XLS_TYPE_DEFCOLWIDTH:
878
                        $this->readDefColWidth();
879
                        break;
880
                    case self::XLS_TYPE_COLINFO:
881
                        $this->readColInfo();
882
                        break;
883
                    case self::XLS_TYPE_DIMENSION:
884
                        $this->readDefault();
885
                        break;
886
                    case self::XLS_TYPE_ROW:
887
                        $this->readRow();
888
                        break;
889
                    case self::XLS_TYPE_DBCELL:
890
                        $this->readDefault();
891
                        break;
892
                    case self::XLS_TYPE_RK:
893
                        $this->readRk();
894
                        break;
895
                    case self::XLS_TYPE_LABELSST:
896
                        $this->readLabelSst();
897
                        break;
898
                    case self::XLS_TYPE_MULRK:
899
                        $this->readMulRk();
900
                        break;
901
                    case self::XLS_TYPE_NUMBER:
902
                        $this->readNumber();
903
                        break;
904
                    case self::XLS_TYPE_FORMULA:
905
                        $this->readFormula();
906
                        break;
907
                    case self::XLS_TYPE_SHAREDFMLA:
908
                        $this->readSharedFmla();
909
                        break;
910
                    case self::XLS_TYPE_BOOLERR:
911
                        $this->readBoolErr();
912
                        break;
913
                    case self::XLS_TYPE_MULBLANK:
914
                        $this->readMulBlank();
915
                        break;
916
                    case self::XLS_TYPE_LABEL:
917
                        $this->readLabel();
918
                        break;
919
                    case self::XLS_TYPE_BLANK:
920
                        $this->readBlank();
921
                        break;
922
                    case self::XLS_TYPE_MSODRAWING:
923
                        $this->readMsoDrawing();
924
                        break;
925
                    case self::XLS_TYPE_OBJ:
926
                        $this->readObj();
927
                        break;
928
                    case self::XLS_TYPE_WINDOW2:
929
                        $this->readWindow2();
930
                        break;
931
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
932
                        $this->readPageLayoutView();
933
                        break;
934
                    case self::XLS_TYPE_SCL:
935
                        $this->readScl();
936
                        break;
937
                    case self::XLS_TYPE_PANE:
938
                        $this->readPane();
939
                        break;
940
                    case self::XLS_TYPE_SELECTION:
941
                        $this->readSelection();
942
                        break;
943
                    case self::XLS_TYPE_MERGEDCELLS:
944
                        $this->readMergedCells();
945
                        break;
946
                    case self::XLS_TYPE_HYPERLINK:
947
                        $this->readHyperLink();
948
                        break;
949
                    case self::XLS_TYPE_DATAVALIDATIONS:
950
                        $this->readDataValidations();
951
                        break;
952
                    case self::XLS_TYPE_DATAVALIDATION:
953
                        $this->readDataValidation();
954
                        break;
955
                    case self::XLS_TYPE_SHEETLAYOUT:
956
                        $this->readSheetLayout();
957
                        break;
958
                    case self::XLS_TYPE_SHEETPROTECTION:
959
                        $this->readSheetProtection();
960
                        break;
961
                    case self::XLS_TYPE_RANGEPROTECTION:
962
                        $this->readRangeProtection();
963
                        break;
964
                    case self::XLS_TYPE_NOTE:
965
                        $this->readNote();
966
                        break;
967
                    //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...
968
                    case self::XLS_TYPE_TXO:
969
                        $this->readTextObject();
970
                        break;
971
                    case self::XLS_TYPE_CONTINUE:
972
                        $this->readContinue();
973
                        break;
974
                    case self::XLS_TYPE_EOF:
975
                        $this->readDefault();
976
                        break 2;
977
                    default:
978
                        $this->readDefault();
979
                        break;
980
                }
981
            }
982
983
            // treat MSODRAWING records, sheet-level Escher
984
            if (!$this->readDataOnly && $this->drawingData) {
985
                $escherWorksheet = new \PhpSpreadsheet\Shared\Escher();
986
                $reader = new Excel5\Escher($escherWorksheet);
987
                $escherWorksheet = $reader->load($this->drawingData);
988
989
                // get all spContainers in one long array, so they can be mapped to OBJ records
990
                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
991
            }
992
993
            // treat OBJ records
994
            foreach ($this->objs as $n => $obj) {
995
//                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...
996
//                var_dump($obj);
997
//                echo '<br />';
998
999
                // the first shape container never has a corresponding OBJ record, hence $n + 1
1000
                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
1001
                    $spContainer = $allSpContainers[$n + 1];
1002
1003
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
1004
                    if ($spContainer->getNestingLevel() > 1) {
1005
                        continue;
1006
                    }
1007
1008
                    // calculate the width and height of the shape
1009
                    list($startColumn, $startRow) = \PhpSpreadsheet\Cell::coordinateFromString($spContainer->getStartCoordinates());
1010
                    list($endColumn, $endRow) = \PhpSpreadsheet\Cell::coordinateFromString($spContainer->getEndCoordinates());
1011
1012
                    $startOffsetX = $spContainer->getStartOffsetX();
1013
                    $startOffsetY = $spContainer->getStartOffsetY();
1014
                    $endOffsetX = $spContainer->getEndOffsetX();
1015
                    $endOffsetY = $spContainer->getEndOffsetY();
1016
1017
                    $width = \PhpSpreadsheet\Shared\Excel5::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
1018
                    $height = \PhpSpreadsheet\Shared\Excel5::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
1019
1020
                    // calculate offsetX and offsetY of the shape
1021
                    $offsetX = $startOffsetX * \PhpSpreadsheet\Shared\Excel5::sizeCol($this->phpSheet, $startColumn) / 1024;
1022
                    $offsetY = $startOffsetY * \PhpSpreadsheet\Shared\Excel5::sizeRow($this->phpSheet, $startRow) / 256;
1023
1024
                    switch ($obj['otObjType']) {
1025
                        case 0x19:
1026
                            // 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...
1027
//                            echo 'Cell Annotation Object<br />';
1028
//                            echo 'Object ID is ', $obj['idObjID'],'<br />';
1029
                            if (isset($this->cellNotes[$obj['idObjID']])) {
1030
                                $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...
1031
1032 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...
1033
                                    $textObject = $this->textObjects[$obj['idObjID']];
1034
                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
1035
                                }
1036
                            }
1037
                            break;
1038
                        case 0x08:
1039
//                            echo 'Picture Object<br />';
1040
                            // picture
1041
                            // get index to BSE entry (1-based)
1042
                            $BSEindex = $spContainer->getOPT(0x0104);
1043
                            $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
0 ignored issues
show
Bug introduced by
The variable $escherWorkbook does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1044
                            $BSE = $BSECollection[$BSEindex - 1];
1045
                            $blipType = $BSE->getBlipType();
1046
1047
                            // need check because some blip types are not supported by Escher reader such as EMF
1048
                            if ($blip = $BSE->getBlip()) {
1049
                                $ih = imagecreatefromstring($blip->getData());
1050
                                $drawing = new \PhpSpreadsheet\Worksheet\MemoryDrawing();
1051
                                $drawing->setImageResource($ih);
1052
1053
                                // width, height, offsetX, offsetY
1054
                                $drawing->setResizeProportional(false);
1055
                                $drawing->setWidth($width);
1056
                                $drawing->setHeight($height);
1057
                                $drawing->setOffsetX($offsetX);
1058
                                $drawing->setOffsetY($offsetY);
1059
1060
                                switch ($blipType) {
1061
                                    case \PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_JPEG:
1062
                                        $drawing->setRenderingFunction(\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_JPEG);
1063
                                        $drawing->setMimeType(\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_JPEG);
1064
                                        break;
1065
                                    case \PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_PNG:
1066
                                        $drawing->setRenderingFunction(\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_PNG);
1067
                                        $drawing->setMimeType(\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_PNG);
1068
                                        break;
1069
                                }
1070
1071
                                $drawing->setWorksheet($this->phpSheet);
1072
                                $drawing->setCoordinates($spContainer->getStartCoordinates());
1073
                            }
1074
                            break;
1075
                        default:
1076
                            // other object type
1077
                            break;
1078
                    }
1079
                }
1080
            }
1081
1082
            // treat SHAREDFMLA records
1083
            if ($this->version == self::XLS_BIFF8) {
1084
                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
1085
                    list($column, $row) = \PhpSpreadsheet\Cell::coordinateFromString($cell);
1086
                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
1087
                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
1088
                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, \PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
1089
                    }
1090
                }
1091
            }
1092
1093
            if (!empty($this->cellNotes)) {
1094
                foreach ($this->cellNotes as $note => $noteDetails) {
1095
                    if (!isset($noteDetails['objTextData'])) {
1096 View Code Duplication
                        if (isset($this->textObjects[$note])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1097
                            $textObject = $this->textObjects[$note];
1098
                            $noteDetails['objTextData'] = $textObject;
1099
                        } else {
1100
                            $noteDetails['objTextData']['text'] = '';
1101
                        }
1102
                    }
1103
//                    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...
1104
//                    var_dump($noteDetails);
1105
//                    echo '<br />';
1106
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
1107
                    $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
1108
                }
1109
            }
1110
        }
1111
1112
        // add the named ranges (defined names)
1113
        foreach ($this->definedname as $definedName) {
1114
            if ($definedName['isBuiltInName']) {
1115
                switch ($definedName['name']) {
1116
                    case pack('C', 0x06):
1117
                        // print area
1118
                        //    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...
1119
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1120
1121
                        $extractedRanges = array();
1122
                        foreach ($ranges as $range) {
1123
                            // $range should look like one of these
1124
                            //        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...
1125
                            //        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...
1126
                            $explodes = explode('!', $range);    // FIXME: what if sheetname contains exclamation mark?
1127
                            $sheetName = trim($explodes[0], "'");
1128
                            if (count($explodes) == 2) {
1129
                                if (strpos($explodes[1], ':') === false) {
1130
                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
1131
                                }
1132
                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
1133
                            }
1134
                        }
1135
                        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...
1136
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
1137
                        }
1138
                        break;
1139
                    case pack('C', 0x07):
1140
                        // print titles (repeating rows)
1141
                        // Assuming BIFF8, there are 3 cases
1142
                        // 1. repeating rows
1143
                        //        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...
1144
                        //        rows 1-2 repeat
1145
                        // 2. repeating columns
1146
                        //        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...
1147
                        //        columns A-B repeat
1148
                        // 3. both repeating rows and repeating columns
1149
                        //        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...
1150
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1151
                        foreach ($ranges as $range) {
1152
                            // $range should look like this one of these
1153
                            //        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...
1154
                            //        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...
1155
                            $explodes = explode('!', $range);
1156
                            if (count($explodes) == 2) {
1157
                                if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
1158
                                    $extractedRange = $explodes[1];
1159
                                    $extractedRange = str_replace('$', '', $extractedRange);
1160
1161
                                    $coordinateStrings = explode(':', $extractedRange);
1162
                                    if (count($coordinateStrings) == 2) {
1163
                                        list($firstColumn, $firstRow) = \PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[0]);
1164
                                        list($lastColumn, $lastRow) = \PhpSpreadsheet\Cell::coordinateFromString($coordinateStrings[1]);
1165
1166
                                        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...
1167
                                            // then we have repeating rows
1168
                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
1169
                                        } 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...
1170
                                            // then we have repeating columns
1171
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
1172
                                        }
1173
                                    }
1174
                                }
1175
                            }
1176
                        }
1177
                        break;
1178
                }
1179
            } else {
1180
                // Extract range
1181
                $explodes = explode('!', $definedName['formula']);
1182
1183
                if (count($explodes) == 2) {
1184
                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1185
                        ($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...
1186
                        $extractedRange = $explodes[1];
1187
                        $extractedRange = str_replace('$', '', $extractedRange);
1188
1189
                        $localOnly = ($definedName['scope'] == 0) ? false : true;
1190
1191
                        $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
1192
1193
                        $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...
1194
                    }
1195
                } 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...
1196
                    //    Named Value
1197
                    //    TODO Provide support for named values
1198
                }
1199
            }
1200
        }
1201
        $this->data = null;
1202
1203
        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...
1204
    }
1205
1206
    /**
1207
     * Read record data from stream, decrypting as required
1208
     *
1209
     * @param string $data   Data stream to read from
1210
     * @param int    $pos    Position to start reading from
1211
     * @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...
1212
     *
1213
     * @return string Record data
1214
     */
1215
    private function readRecordData($data, $pos, $len)
1216
    {
1217
        $data = substr($data, $pos, $len);
1218
1219
        // File not encrypted, or record before encryption start point
1220
        if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
1221
            return $data;
1222
        }
1223
1224
        $recordData = '';
1225
        if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
1226
            $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
1227
            $block = floor($pos / self::REKEY_BLOCK);
1228
            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
1229
1230
            // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
1231
            // at a point earlier in the current block, re-use it as we can save some time.
1232
            if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
1233
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1234
                $step = $pos % self::REKEY_BLOCK;
1235
            } else {
1236
                $step = $pos - $this->rc4Pos;
1237
            }
1238
            $this->rc4Key->RC4(str_repeat("\0", $step));
1239
1240
            // Decrypt record data (re-keying at the end of every block)
1241
            while ($block != $endBlock) {
1242
                $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
1243
                $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
1244
                $data = substr($data, $step);
1245
                $pos += $step;
1246
                $len -= $step;
1247
                $block++;
1248
                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
1249
            }
1250
            $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
1251
1252
            // Keep track of the position of this decryptor.
1253
            // We'll try and re-use it later if we can to speed things up
1254
            $this->rc4Pos = $pos + $len;
1255
        } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
1256
            throw new Exception('XOr encryption not supported');
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
    /**
1284
     * Read summary information
1285
     */
1286
    private function readSummaryInformation()
1287
    {
1288
        if (!isset($this->summaryInformation)) {
1289
            return;
1290
        }
1291
1292
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1293
        // 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...
1294
        // offset: 4; size: 2; OS version
1295
        // offset: 6; size: 2; OS indicator
1296
        // offset: 8; size: 16
1297
        // offset: 24; size: 4; section count
1298
        $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...
1299
1300
        // 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
1301
        // offset: 44; size: 4
1302
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1303
1304
        // section header
1305
        // offset: $secOffset; size: 4; section length
1306
        $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...
1307
1308
        // offset: $secOffset+4; size: 4; property count
1309
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset+4);
1310
1311
        // initialize code page (used to resolve string values)
1312
        $codePage = 'CP1252';
1313
1314
        // 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...
1315
        // loop through property decarations and properties
1316
        for ($i = 0; $i < $countProperties; ++$i) {
1317
            // 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...
1318
            $id = self::getInt4d($this->summaryInformation, ($secOffset+8) + (8 * $i));
1319
1320
            // Use value of property id as appropriate
1321
            // 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...
1322
            $offset = self::getInt4d($this->summaryInformation, ($secOffset+12) + (8 * $i));
1323
1324
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1325
1326
            // initialize property value
1327
            $value = null;
1328
1329
            // extract property value based on property type
1330
            switch ($type) {
1331
                case 0x02: // 2 byte signed integer
1332
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1333
                    break;
1334
                case 0x03: // 4 byte signed integer
1335
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1336
                    break;
1337
                case 0x13: // 4 byte unsigned integer
1338
                    // not needed yet, fix later if necessary
1339
                    break;
1340 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...
1341
                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1342
                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1343
                    $value = \PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1344
                    $value = rtrim($value);
1345
                    break;
1346 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...
1347
                    // PHP-time
1348
                    $value = \PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1349
                    break;
1350
                case 0x47: // Clipboard format
1351
                    // not needed yet, fix later if necessary
1352
                    break;
1353
            }
1354
1355
            switch ($id) {
1356
                case 0x01:    //    Code Page
1357
                    $codePage = \PhpSpreadsheet\Shared\CodePage::numberToName($value);
1358
                    break;
1359
                case 0x02:    //    Title
1360
                    $this->spreadsheet->getProperties()->setTitle($value);
1361
                    break;
1362
                case 0x03:    //    Subject
1363
                    $this->spreadsheet->getProperties()->setSubject($value);
1364
                    break;
1365
                case 0x04:    //    Author (Creator)
1366
                    $this->spreadsheet->getProperties()->setCreator($value);
1367
                    break;
1368
                case 0x05:    //    Keywords
1369
                    $this->spreadsheet->getProperties()->setKeywords($value);
1370
                    break;
1371
                case 0x06:    //    Comments (Description)
1372
                    $this->spreadsheet->getProperties()->setDescription($value);
1373
                    break;
1374
                case 0x07:    //    Template
1375
                    //    Not supported by PhpSpreadsheet
1376
                    break;
1377
                case 0x08:    //    Last Saved By (LastModifiedBy)
1378
                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
1379
                    break;
1380
                case 0x09:    //    Revision
1381
                    //    Not supported by PhpSpreadsheet
1382
                    break;
1383
                case 0x0A:    //    Total Editing Time
1384
                    //    Not supported by PhpSpreadsheet
1385
                    break;
1386
                case 0x0B:    //    Last Printed
1387
                    //    Not supported by PhpSpreadsheet
1388
                    break;
1389
                case 0x0C:    //    Created Date/Time
1390
                    $this->spreadsheet->getProperties()->setCreated($value);
1391
                    break;
1392
                case 0x0D:    //    Modified Date/Time
1393
                    $this->spreadsheet->getProperties()->setModified($value);
1394
                    break;
1395
                case 0x0E:    //    Number of Pages
1396
                    //    Not supported by PhpSpreadsheet
1397
                    break;
1398
                case 0x0F:    //    Number of Words
1399
                    //    Not supported by PhpSpreadsheet
1400
                    break;
1401
                case 0x10:    //    Number of Characters
1402
                    //    Not supported by PhpSpreadsheet
1403
                    break;
1404
                case 0x11:    //    Thumbnail
1405
                    //    Not supported by PhpSpreadsheet
1406
                    break;
1407
                case 0x12:    //    Name of creating application
1408
                    //    Not supported by PhpSpreadsheet
1409
                    break;
1410
                case 0x13:    //    Security
1411
                    //    Not supported by PhpSpreadsheet
1412
                    break;
1413
            }
1414
        }
1415
    }
1416
1417
1418
    /**
1419
     * Read additional document summary information
1420
     */
1421
    private function readDocumentSummaryInformation()
1422
    {
1423
        if (!isset($this->documentSummaryInformation)) {
1424
            return;
1425
        }
1426
1427
        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
1428
        //    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...
1429
        //    offset: 4;    size: 2;    OS version
1430
        //    offset: 6;    size: 2;    OS indicator
1431
        //    offset: 8;    size: 16
1432
        //    offset: 24;    size: 4;    section count
1433
        $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...
1434
//        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...
1435
1436
        // 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
1437
        // offset: 44;    size: 4;    first section offset
1438
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1439
//        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...
1440
1441
        //    section header
1442
        //    offset: $secOffset;    size: 4;    section length
1443
        $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...
1444
//        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...
1445
1446
        //    offset: $secOffset+4;    size: 4;    property count
1447
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset+4);
1448
//        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...
1449
1450
        // initialize code page (used to resolve string values)
1451
        $codePage = 'CP1252';
1452
1453
        //    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...
1454
        //    loop through property decarations and properties
1455
        for ($i = 0; $i < $countProperties; ++$i) {
1456
//            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...
1457
            //    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...
1458
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset+8) + (8 * $i));
1459
//            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...
1460
1461
            // Use value of property id as appropriate
1462
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1463
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset+12) + (8 * $i));
1464
1465
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1466
//            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...
1467
1468
            // initialize property value
1469
            $value = null;
1470
1471
            // extract property value based on property type
1472
            switch ($type) {
1473
                case 0x02:    //    2 byte signed integer
1474
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1475
                    break;
1476
                case 0x03:    //    4 byte signed integer
1477
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1478
                    break;
1479
                case 0x0B:  // Boolean
1480
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1481
                    $value = ($value == 0 ? false : true);
1482
                    break;
1483
                case 0x13:    //    4 byte unsigned integer
1484
                    // not needed yet, fix later if necessary
1485
                    break;
1486 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...
1487
                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1488
                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1489
                    $value = \PhpSpreadsheet\Shared\StringHelper::convertEncoding($value, 'UTF-8', $codePage);
1490
                    $value = rtrim($value);
1491
                    break;
1492 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...
1493
                    // PHP-Time
1494
                    $value = \PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1495
                    break;
1496
                case 0x47:    //    Clipboard format
1497
                    // not needed yet, fix later if necessary
1498
                    break;
1499
            }
1500
1501
            switch ($id) {
1502
                case 0x01:    //    Code Page
1503
                    $codePage = \PhpSpreadsheet\Shared\CodePage::numberToName($value);
1504
                    break;
1505
                case 0x02:    //    Category
1506
                    $this->spreadsheet->getProperties()->setCategory($value);
1507
                    break;
1508
                case 0x03:    //    Presentation Target
1509
                    //    Not supported by PhpSpreadsheet
1510
                    break;
1511
                case 0x04:    //    Bytes
1512
                    //    Not supported by PhpSpreadsheet
1513
                    break;
1514
                case 0x05:    //    Lines
1515
                    //    Not supported by PhpSpreadsheet
1516
                    break;
1517
                case 0x06:    //    Paragraphs
1518
                    //    Not supported by PhpSpreadsheet
1519
                    break;
1520
                case 0x07:    //    Slides
1521
                    //    Not supported by PhpSpreadsheet
1522
                    break;
1523
                case 0x08:    //    Notes
1524
                    //    Not supported by PhpSpreadsheet
1525
                    break;
1526
                case 0x09:    //    Hidden Slides
1527
                    //    Not supported by PhpSpreadsheet
1528
                    break;
1529
                case 0x0A:    //    MM Clips
1530
                    //    Not supported by PhpSpreadsheet
1531
                    break;
1532
                case 0x0B:    //    Scale Crop
1533
                    //    Not supported by PhpSpreadsheet
1534
                    break;
1535
                case 0x0C:    //    Heading Pairs
1536
                    //    Not supported by PhpSpreadsheet
1537
                    break;
1538
                case 0x0D:    //    Titles of Parts
1539
                    //    Not supported by PhpSpreadsheet
1540
                    break;
1541
                case 0x0E:    //    Manager
1542
                    $this->spreadsheet->getProperties()->setManager($value);
1543
                    break;
1544
                case 0x0F:    //    Company
1545
                    $this->spreadsheet->getProperties()->setCompany($value);
1546
                    break;
1547
                case 0x10:    //    Links up-to-date
1548
                    //    Not supported by PhpSpreadsheet
1549
                    break;
1550
            }
1551
        }
1552
    }
1553
1554
1555
    /**
1556
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1557
     */
1558
    private function readDefault()
1559
    {
1560
        $length = self::getInt2d($this->data, $this->pos + 2);
1561
//        $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...
1562
1563
        // move stream pointer to next record
1564
        $this->pos += 4 + $length;
1565
    }
1566
1567
1568
    /**
1569
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1570
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1571
     */
1572
    private function readNote()
1573
    {
1574
//        echo '<b>Read Cell Annotation</b><br />';
1575
        $length = self::getInt2d($this->data, $this->pos + 2);
1576
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1577
1578
        // move stream pointer to next record
1579
        $this->pos += 4 + $length;
1580
1581
        if ($this->readDataOnly) {
1582
            return;
1583
        }
1584
1585
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1586
        if ($this->version == self::XLS_BIFF8) {
1587
            $noteObjID = self::getInt2d($recordData, 6);
1588
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1589
            $noteAuthor = $noteAuthor['value'];
1590
//            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...
1591
//            echo 'Note Object ID=', $noteObjID,'<br />';
1592
//            echo 'Note Author=', $noteAuthor,'<hr />';
1593
//
1594
            $this->cellNotes[$noteObjID] = array(
1595
                'cellRef'   => $cellAddress,
1596
                'objectID'  => $noteObjID,
1597
                'author'    => $noteAuthor
1598
            );
1599
        } else {
1600
            $extension = false;
1601
            if ($cellAddress == '$B$65536') {
1602
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1603
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1604
                //        max 2048 bytes will probably throw a wobbly.
1605
                $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...
1606
                $extension = true;
1607
                $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...
1608
            }
1609
//            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...
1610
1611
            $cellAddress = str_replace('$', '', $cellAddress);
1612
            $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...
1613
            $noteText = trim(substr($recordData, 6));
1614
//            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...
1615
//            echo 'Note Text=', $noteText,'<br />';
1616
1617
            if ($extension) {
1618
                //    Concatenate this extension with the currently set comment for the cell
1619
                $comment = $this->phpSheet->getComment($cellAddress);
1620
                $commentText = $comment->getText()->getPlainText();
1621
                $comment->setText($this->parseRichText($commentText.$noteText));
1622
            } else {
1623
                //    Set comment for the cell
1624
                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
1625
//                                                    ->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...
1626
            }
1627
        }
1628
    }
1629
1630
1631
    /**
1632
     *    The TEXT Object record contains the text associated with a cell annotation.
1633
     */
1634
    private function readTextObject()
1635
    {
1636
        $length = self::getInt2d($this->data, $this->pos + 2);
1637
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1638
1639
        // move stream pointer to next record
1640
        $this->pos += 4 + $length;
1641
1642
        if ($this->readDataOnly) {
1643
            return;
1644
        }
1645
1646
        // recordData consists of an array of subrecords looking like this:
1647
        //    grbit: 2 bytes; Option Flags
1648
        //    rot: 2 bytes; rotation
1649
        //    cchText: 2 bytes; length of the text (in the first continue record)
1650
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1651
        // followed by the continuation records containing the actual text and formatting
1652
        $grbitOpts  = self::getInt2d($recordData, 0);
1653
        $rot        = self::getInt2d($recordData, 2);
1654
        $cchText    = self::getInt2d($recordData, 10);
1655
        $cbRuns     = self::getInt2d($recordData, 12);
1656
        $text       = $this->getSplicedRecordData();
1657
1658
        $this->textObjects[$this->textObjRef] = array(
1659
            'text'      => substr($text["recordData"], $text["spliceOffsets"][0]+1, $cchText),
1660
            'format'    => substr($text["recordData"], $text["spliceOffsets"][1], $cbRuns),
1661
            'alignment' => $grbitOpts,
1662
            'rotation'  => $rot
1663
        );
1664
1665
//        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...
1666
//        var_dump($this->textObjects[$this->textObjRef]);
1667
//        echo '<br />';
1668
    }
1669
1670
1671
    /**
1672
     * Read BOF
1673
     */
1674
    private function readBof()
1675
    {
1676
        $length = self::getInt2d($this->data, $this->pos + 2);
1677
        $recordData = substr($this->data, $this->pos + 4, $length);
1678
1679
        // move stream pointer to next record
1680
        $this->pos += 4 + $length;
1681
1682
        // offset: 2; size: 2; type of the following data
1683
        $substreamType = self::getInt2d($recordData, 2);
1684
1685
        switch ($substreamType) {
1686
            case self::XLS_WORKBOOKGLOBALS:
1687
                $version = self::getInt2d($recordData, 0);
1688
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1689
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1690
                }
1691
                $this->version = $version;
1692
                break;
1693
            case self::XLS_WORKSHEET:
1694
                // do not use this version information for anything
1695
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1696
                break;
1697
            default:
1698
                // substream, e.g. chart
1699
                // just skip the entire substream
1700
                do {
1701
                    $code = self::getInt2d($this->data, $this->pos);
1702
                    $this->readDefault();
1703
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1704
                break;
1705
        }
1706
    }
1707
1708
1709
    /**
1710
     * FILEPASS
1711
     *
1712
     * This record is part of the File Protection Block. It
1713
     * contains information about the read/write password of the
1714
     * file. All record contents following this record will be
1715
     * encrypted.
1716
     *
1717
     * --    "OpenOffice.org's Documentation of the Microsoft
1718
     *         Excel File Format"
1719
     *
1720
     * The decryption functions and objects used from here on in
1721
     * are based on the source of Spreadsheet-ParseExcel:
1722
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1723
     */
1724
    private function readFilepass()
1725
    {
1726
        $length = self::getInt2d($this->data, $this->pos + 2);
1727
1728
        if ($length != 54) {
1729
            throw new Exception('Unexpected file pass record length');
1730
        }
1731
1732
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1733
1734
        // move stream pointer to next record
1735
        $this->pos += 4 + $length;
1736
1737
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1738
            throw new Exception('Decryption password incorrect');
1739
        }
1740
1741
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1742
1743
        // Decryption required from the record after next onwards
1744
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1745
    }
1746
1747
    /**
1748
     * Make an RC4 decryptor for the given block
1749
     *
1750
     * @var int    $block      Block for which to create decrypto
1751
     * @var string $valContext MD5 context state
1752
     *
1753
     * @return Excel5\RC4
1754
     */
1755
    private function makeKey($block, $valContext)
1756
    {
1757
        $pwarray = str_repeat("\0", 64);
1758
1759
        for ($i = 0; $i < 5; $i++) {
1760
            $pwarray[$i] = $valContext[$i];
1761
        }
1762
1763
        $pwarray[5] = chr($block & 0xff);
1764
        $pwarray[6] = chr(($block >> 8) & 0xff);
1765
        $pwarray[7] = chr(($block >> 16) & 0xff);
1766
        $pwarray[8] = chr(($block >> 24) & 0xff);
1767
1768
        $pwarray[9] = "\x80";
1769
        $pwarray[56] = "\x48";
1770
1771
        $md5 = new Excel5\MD5();
1772
        $md5->add($pwarray);
1773
1774
        $s = $md5->getContext();
1775
        return new Excel5\RC4($s);
1776
    }
1777
1778
    /**
1779
     * Verify RC4 file password
1780
     *
1781
     * @var string $password        Password to check
1782
     * @var string $docid           Document id
1783
     * @var string $salt_data       Salt data
1784
     * @var string $hashedsalt_data Hashed salt data
1785
     * @var string &$valContext     Set to the MD5 context of the value
1786
     *
1787
     * @return bool Success
1788
     */
1789
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1790
    {
1791
        $pwarray = str_repeat("\0", 64);
1792
1793
        for ($i = 0; $i < strlen($password); $i++) {
1794
            $o = ord(substr($password, $i, 1));
1795
            $pwarray[2 * $i] = chr($o & 0xff);
1796
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1797
        }
1798
        $pwarray[2 * $i] = chr(0x80);
1799
        $pwarray[56] = chr(($i << 4) & 0xff);
1800
1801
        $md5 = new Excel5\MD5();
1802
        $md5->add($pwarray);
1803
1804
        $mdContext1 = $md5->getContext();
1805
1806
        $offset = 0;
1807
        $keyoffset = 0;
1808
        $tocopy = 5;
1809
1810
        $md5->reset();
1811
1812
        while ($offset != 16) {
1813
            if ((64 - $offset) < 5) {
1814
                $tocopy = 64 - $offset;
1815
            }
1816
            for ($i = 0; $i <= $tocopy; $i++) {
1817
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1818
            }
1819
            $offset += $tocopy;
1820
1821
            if ($offset == 64) {
1822
                $md5->add($pwarray);
1823
                $keyoffset = $tocopy;
1824
                $tocopy = 5 - $tocopy;
1825
                $offset = 0;
1826
                continue;
1827
            }
1828
1829
            $keyoffset = 0;
1830
            $tocopy = 5;
1831
            for ($i = 0; $i < 16; $i++) {
1832
                $pwarray[$offset + $i] = $docid[$i];
1833
            }
1834
            $offset += 16;
1835
        }
1836
1837
        $pwarray[16] = "\x80";
1838
        for ($i = 0; $i < 47; $i++) {
1839
            $pwarray[17 + $i] = "\0";
1840
        }
1841
        $pwarray[56] = "\x80";
1842
        $pwarray[57] = "\x0a";
1843
1844
        $md5->add($pwarray);
1845
        $valContext = $md5->getContext();
1846
1847
        $key = $this->makeKey(0, $valContext);
1848
1849
        $salt = $key->RC4($salt_data);
1850
        $hashedsalt = $key->RC4($hashedsalt_data);
1851
1852
        $salt .= "\x80" . str_repeat("\0", 47);
1853
        $salt[56] = "\x80";
1854
1855
        $md5->reset();
1856
        $md5->add($salt);
1857
        $mdContext2 = $md5->getContext();
1858
1859
        return $mdContext2 == $hashedsalt;
1860
    }
1861
1862
    /**
1863
     * CODEPAGE
1864
     *
1865
     * This record stores the text encoding used to write byte
1866
     * strings, stored as MS Windows code page identifier.
1867
     *
1868
     * --    "OpenOffice.org's Documentation of the Microsoft
1869
     *         Excel File Format"
1870
     */
1871 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...
1872
    {
1873
        $length = self::getInt2d($this->data, $this->pos + 2);
1874
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1875
1876
        // move stream pointer to next record
1877
        $this->pos += 4 + $length;
1878
1879
        // offset: 0; size: 2; code page identifier
1880
        $codepage = self::getInt2d($recordData, 0);
1881
1882
        $this->codepage = \PhpSpreadsheet\Shared\CodePage::numberToName($codepage);
1883
    }
1884
1885
1886
    /**
1887
     * DATEMODE
1888
     *
1889
     * This record specifies the base date for displaying date
1890
     * values. All dates are stored as count of days past this
1891
     * base date. In BIFF2-BIFF4 this record is part of the
1892
     * Calculation Settings Block. In BIFF5-BIFF8 it is
1893
     * stored in the Workbook Globals Substream.
1894
     *
1895
     * --    "OpenOffice.org's Documentation of the Microsoft
1896
     *         Excel File Format"
1897
     */
1898
    private function readDateMode()
1899
    {
1900
        $length = self::getInt2d($this->data, $this->pos + 2);
1901
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1902
1903
        // move stream pointer to next record
1904
        $this->pos += 4 + $length;
1905
1906
        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
1907
        \PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900);
1908
        if (ord($recordData{0}) == 1) {
1909
            \PhpSpreadsheet\Shared\Date::setExcelCalendar(\PhpSpreadsheet\Shared\Date::CALENDAR_MAC_1904);
1910
        }
1911
    }
1912
1913
1914
    /**
1915
     * Read a FONT record
1916
     */
1917
    private function readFont()
1918
    {
1919
        $length = self::getInt2d($this->data, $this->pos + 2);
1920
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1921
1922
        // move stream pointer to next record
1923
        $this->pos += 4 + $length;
1924
1925
        if (!$this->readDataOnly) {
1926
            $objFont = new \PhpSpreadsheet\Style\Font();
1927
1928
            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
1929
            $size = self::getInt2d($recordData, 0);
1930
            $objFont->setSize($size / 20);
1931
1932
            // offset: 2; size: 2; option flags
1933
            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
1934
            // bit: 1; mask 0x0002; italic
1935
            $isItalic = (0x0002 & self::getInt2d($recordData, 2)) >> 1;
1936
            if ($isItalic) {
1937
                $objFont->setItalic(true);
1938
            }
1939
1940
            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
1941
            // bit: 3; mask 0x0008; strike
1942
            $isStrike = (0x0008 & self::getInt2d($recordData, 2)) >> 3;
1943
            if ($isStrike) {
1944
                $objFont->setStrikethrough(true);
1945
            }
1946
1947
            // offset: 4; size: 2; colour index
1948
            $colorIndex = self::getInt2d($recordData, 4);
1949
            $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...
1950
1951
            // offset: 6; size: 2; font weight
1952
            $weight = self::getInt2d($recordData, 6);
1953
            switch ($weight) {
1954
                case 0x02BC:
1955
                    $objFont->setBold(true);
1956
                    break;
1957
            }
1958
1959
            // offset: 8; size: 2; escapement type
1960
            $escapement = self::getInt2d($recordData, 8);
1961
            switch ($escapement) {
1962
                case 0x0001:
1963
                    $objFont->setSuperScript(true);
1964
                    break;
1965
                case 0x0002:
1966
                    $objFont->setSubScript(true);
1967
                    break;
1968
            }
1969
1970
            // offset: 10; size: 1; underline type
1971
            $underlineType = ord($recordData{10});
1972
            switch ($underlineType) {
1973
                case 0x00:
1974
                    break; // no underline
1975
                case 0x01:
1976
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1977
                    break;
1978
                case 0x02:
1979
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE);
1980
                    break;
1981
                case 0x21:
1982
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING);
1983
                    break;
1984
                case 0x22:
1985
                    $objFont->setUnderline(\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING);
1986
                    break;
1987
            }
1988
1989
            // offset: 11; size: 1; font family
1990
            // offset: 12; size: 1; character set
1991
            // offset: 13; size: 1; not used
1992
            // offset: 14; size: var; font name
1993 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...
1994
                $string = self::readUnicodeStringShort(substr($recordData, 14));
1995
            } else {
1996
                $string = $this->readByteStringShort(substr($recordData, 14));
1997
            }
1998
            $objFont->setName($string['value']);
1999
2000
            $this->objFonts[] = $objFont;
2001
        }
2002
    }
2003
2004
2005
    /**
2006
     * FORMAT
2007
     *
2008
     * This record contains information about a number format.
2009
     * All FORMAT records occur together in a sequential list.
2010
     *
2011
     * In BIFF2-BIFF4 other records referencing a FORMAT record
2012
     * contain a zero-based index into this list. From BIFF5 on
2013
     * the FORMAT record contains the index itself that will be
2014
     * used by other records.
2015
     *
2016
     * --    "OpenOffice.org's Documentation of the Microsoft
2017
     *         Excel File Format"
2018
     */
2019
    private function readFormat()
2020
    {
2021
        $length = self::getInt2d($this->data, $this->pos + 2);
2022
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2023
2024
        // move stream pointer to next record
2025
        $this->pos += 4 + $length;
2026
2027
        if (!$this->readDataOnly) {
2028
            $indexCode = self::getInt2d($recordData, 0);
2029
2030 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...
2031
                $string = self::readUnicodeStringLong(substr($recordData, 2));
2032
            } else {
2033
                // BIFF7
2034
                $string = $this->readByteStringShort(substr($recordData, 2));
2035
            }
2036
2037
            $formatString = $string['value'];
2038
            $this->formats[$indexCode] = $formatString;
2039
        }
2040
    }
2041
2042
2043
    /**
2044
     * XF - Extended Format
2045
     *
2046
     * This record contains formatting information for cells, rows, columns or styles.
2047
     * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
2048
     * and 1 cell XF.
2049
     * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
2050
     * and XF record 15 is a cell XF
2051
     * We only read the first cell style XF and skip the remaining cell style XF records
2052
     * We read all cell XF records.
2053
     *
2054
     * --    "OpenOffice.org's Documentation of the Microsoft
2055
     *         Excel File Format"
2056
     */
2057
    private function readXf()
2058
    {
2059
        $length = self::getInt2d($this->data, $this->pos + 2);
2060
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2061
2062
        // move stream pointer to next record
2063
        $this->pos += 4 + $length;
2064
2065
        $objStyle = new \PhpSpreadsheet\Style();
2066
2067
        if (!$this->readDataOnly) {
2068
            // offset:  0; size: 2; Index to FONT record
2069
            if (self::getInt2d($recordData, 0) < 4) {
2070
                $fontIndex = self::getInt2d($recordData, 0);
2071
            } else {
2072
                // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
2073
                // check the OpenOffice documentation of the FONT record
2074
                $fontIndex = self::getInt2d($recordData, 0) - 1;
2075
            }
2076
            $objStyle->setFont($this->objFonts[$fontIndex]);
2077
2078
            // offset:  2; size: 2; Index to FORMAT record
2079
            $numberFormatIndex = self::getInt2d($recordData, 2);
2080
            if (isset($this->formats[$numberFormatIndex])) {
2081
                // then we have user-defined format code
2082
                $numberformat = array('code' => $this->formats[$numberFormatIndex]);
2083
            } elseif (($code = \PhpSpreadsheet\Style\NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
2084
                // then we have built-in format code
2085
                $numberformat = array('code' => $code);
2086
            } else {
2087
                // we set the general format code
2088
                $numberformat = array('code' => 'General');
2089
            }
2090
            $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
2091
2092
            // offset:  4; size: 2; XF type, cell protection, and parent style XF
2093
            // 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...
2094
            $xfTypeProt = self::getInt2d($recordData, 4);
2095
            // bit 0; mask 0x01; 1 = cell is locked
2096
            $isLocked = (0x01 & $xfTypeProt) >> 0;
2097
            $objStyle->getProtection()->setLocked($isLocked ? \PhpSpreadsheet\Style\Protection::PROTECTION_INHERIT : \PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2098
2099
            // bit 1; mask 0x02; 1 = Formula is hidden
2100
            $isHidden = (0x02 & $xfTypeProt) >> 1;
2101
            $objStyle->getProtection()->setHidden($isHidden ? \PhpSpreadsheet\Style\Protection::PROTECTION_PROTECTED : \PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
2102
2103
            // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
2104
            $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
2105
2106
            // offset:  6; size: 1; Alignment and text break
2107
            // bit 2-0, mask 0x07; horizontal alignment
2108
            $horAlign = (0x07 & ord($recordData{6})) >> 0;
2109
            switch ($horAlign) {
2110
                case 0:
2111
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL);
2112
                    break;
2113
                case 1:
2114
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
2115
                    break;
2116
                case 2:
2117
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
2118
                    break;
2119
                case 3:
2120
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT);
2121
                    break;
2122
                case 4:
2123
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_FILL);
2124
                    break;
2125
                case 5:
2126
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY);
2127
                    break;
2128
                case 6:
2129
                    $objStyle->getAlignment()->setHorizontal(\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS);
2130
                    break;
2131
            }
2132
            // bit 3, mask 0x08; wrap text
2133
            $wrapText = (0x08 & ord($recordData{6})) >> 3;
2134
            switch ($wrapText) {
2135
                case 0:
2136
                    $objStyle->getAlignment()->setWrapText(false);
2137
                    break;
2138
                case 1:
2139
                    $objStyle->getAlignment()->setWrapText(true);
2140
                    break;
2141
            }
2142
            // bit 6-4, mask 0x70; vertical alignment
2143
            $vertAlign = (0x70 & ord($recordData{6})) >> 4;
2144
            switch ($vertAlign) {
2145
                case 0:
2146
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP);
2147
                    break;
2148
                case 1:
2149
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
2150
                    break;
2151
                case 2:
2152
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM);
2153
                    break;
2154
                case 3:
2155
                    $objStyle->getAlignment()->setVertical(\PhpSpreadsheet\Style\Alignment::VERTICAL_JUSTIFY);
2156
                    break;
2157
            }
2158
2159
            if ($this->version == self::XLS_BIFF8) {
2160
                // offset:  7; size: 1; XF_ROTATION: Text rotation angle
2161
                $angle = ord($recordData{7});
2162
                $rotation = 0;
2163
                if ($angle <= 90) {
2164
                    $rotation = $angle;
2165
                } elseif ($angle <= 180) {
2166
                    $rotation = 90 - $angle;
2167
                } elseif ($angle == 255) {
2168
                    $rotation = -165;
2169
                }
2170
                $objStyle->getAlignment()->setTextRotation($rotation);
2171
2172
                // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
2173
                // bit: 3-0; mask: 0x0F; indent level
2174
                $indent = (0x0F & ord($recordData{8})) >> 0;
2175
                $objStyle->getAlignment()->setIndent($indent);
2176
2177
                // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
2178
                $shrinkToFit = (0x10 & ord($recordData{8})) >> 4;
2179
                switch ($shrinkToFit) {
2180
                    case 0:
2181
                        $objStyle->getAlignment()->setShrinkToFit(false);
2182
                        break;
2183
                    case 1:
2184
                        $objStyle->getAlignment()->setShrinkToFit(true);
2185
                        break;
2186
                }
2187
2188
                // offset:  9; size: 1; Flags used for attribute groups
2189
2190
                // offset: 10; size: 4; Cell border lines and background area
2191
                // bit: 3-0; mask: 0x0000000F; left style
2192 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...
2193
                    $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
2194
                }
2195
                // bit: 7-4; mask: 0x000000F0; right style
2196 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...
2197
                    $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
2198
                }
2199
                // bit: 11-8; mask: 0x00000F00; top style
2200 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...
2201
                    $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
2202
                }
2203
                // bit: 15-12; mask: 0x0000F000; bottom style
2204 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...
2205
                    $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
2206
                }
2207
                // bit: 22-16; mask: 0x007F0000; left color
2208
                $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...
2209
2210
                // bit: 29-23; mask: 0x3F800000; right color
2211
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2212
2213
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2214
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2215
2216
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2217
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2218
2219
                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...
2220
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_NONE);
2221
                } 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...
2222
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_UP);
2223
                } 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...
2224
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_DOWN);
2225
                } 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...
2226
                    $objStyle->getBorders()->setDiagonalDirection(\PhpSpreadsheet\Style\Borders::DIAGONAL_BOTH);
2227
                }
2228
2229
                // 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...
2230
                // bit: 6-0; mask: 0x0000007F; top color
2231
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2232
2233
                // bit: 13-7; mask: 0x00003F80; bottom color
2234
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2235
2236
                // bit: 20-14; mask: 0x001FC000; diagonal color
2237
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2238
2239
                // bit: 24-21; mask: 0x01E00000; diagonal style
2240 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...
2241
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2242
                }
2243
2244
                // bit: 31-26; mask: 0xFC000000 fill pattern
2245
                if ($fillType = Excel5\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2246
                    $objStyle->getFill()->setFillType($fillType);
2247
                }
2248
                // offset: 18; size: 2; pattern and background colour
2249
                // bit: 6-0; mask: 0x007F; color index for pattern color
2250
                $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...
2251
2252
                // bit: 13-7; mask: 0x3F80; color index for pattern background
2253
                $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...
2254
            } else {
2255
                // BIFF5
2256
2257
                // offset: 7; size: 1; Text orientation and flags
2258
                $orientationAndFlags = ord($recordData{7});
2259
2260
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2261
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2262
                switch ($xfOrientation) {
2263
                    case 0:
2264
                        $objStyle->getAlignment()->setTextRotation(0);
2265
                        break;
2266
                    case 1:
2267
                        $objStyle->getAlignment()->setTextRotation(-165);
2268
                        break;
2269
                    case 2:
2270
                        $objStyle->getAlignment()->setTextRotation(90);
2271
                        break;
2272
                    case 3:
2273
                        $objStyle->getAlignment()->setTextRotation(-90);
2274
                        break;
2275
                }
2276
2277
                // offset: 8; size: 4; cell border lines and background area
2278
                $borderAndBackground = self::getInt4d($recordData, 8);
2279
2280
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2281
                $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...
2282
2283
                // bit: 13-7; mask: 0x00003F80; color index for pattern background
2284
                $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...
2285
2286
                // bit: 21-16; mask: 0x003F0000; fill pattern
2287
                $objStyle->getFill()->setFillType(Excel5\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2288
2289
                // bit: 24-22; mask: 0x01C00000; bottom line style
2290
                $objStyle->getBorders()->getBottom()->setBorderStyle(Excel5\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2291
2292
                // bit: 31-25; mask: 0xFE000000; bottom line color
2293
                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2294
2295
                // offset: 12; size: 4; cell border lines
2296
                $borderLines = self::getInt4d($recordData, 12);
2297
2298
                // bit: 2-0; mask: 0x00000007; top line style
2299
                $objStyle->getBorders()->getTop()->setBorderStyle(Excel5\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2300
2301
                // bit: 5-3; mask: 0x00000038; left line style
2302
                $objStyle->getBorders()->getLeft()->setBorderStyle(Excel5\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2303
2304
                // bit: 8-6; mask: 0x000001C0; right line style
2305
                $objStyle->getBorders()->getRight()->setBorderStyle(Excel5\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2306
2307
                // bit: 15-9; mask: 0x0000FE00; top line color index
2308
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2309
2310
                // bit: 22-16; mask: 0x007F0000; left line color index
2311
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2312
2313
                // bit: 29-23; mask: 0x3F800000; right line color index
2314
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2315
            }
2316
2317
            // add cellStyleXf or cellXf and update mapping
2318
            if ($isCellStyleXf) {
2319
                // we only read one style XF record which is always the first
2320
                if ($this->xfIndex == 0) {
2321
                    $this->spreadsheet->addCellStyleXf($objStyle);
2322
                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
2323
                }
2324
            } else {
2325
                // we read all cell XF records
2326
                $this->spreadsheet->addCellXf($objStyle);
2327
                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2328
            }
2329
2330
            // update XF index for when we read next record
2331
            ++$this->xfIndex;
2332
        }
2333
    }
2334
2335
2336
    /**
2337
     *
2338
     */
2339
    private function readXfExt()
2340
    {
2341
        $length = self::getInt2d($this->data, $this->pos + 2);
2342
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2343
2344
        // move stream pointer to next record
2345
        $this->pos += 4 + $length;
2346
2347
        if (!$this->readDataOnly) {
2348
            // offset: 0; size: 2; 0x087D = repeated header
2349
2350
            // offset: 2; size: 2
2351
2352
            // offset: 4; size: 8; not used
2353
2354
            // offset: 12; size: 2; record version
2355
2356
            // offset: 14; size: 2; index to XF record which this record modifies
2357
            $ixfe = self::getInt2d($recordData, 14);
2358
2359
            // offset: 16; size: 2; not used
2360
2361
            // offset: 18; size: 2; number of extension properties that follow
2362
            $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...
2363
2364
            // start reading the actual extension data
2365
            $offset = 20;
2366
            while ($offset < $length) {
2367
                // extension type
2368
                $extType = self::getInt2d($recordData, $offset);
2369
2370
                // extension length
2371
                $cb = self::getInt2d($recordData, $offset + 2);
2372
2373
                // extension data
2374
                $extData = substr($recordData, $offset + 4, $cb);
2375
2376
                switch ($extType) {
2377 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...
2378
                        $xclfType  = self::getInt2d($extData, 0); // color type
2379
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2380
2381
                        if ($xclfType == 2) {
2382
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2383
2384
                            // modify the relevant style property
2385
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2386
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2387
                                $fill->getStartColor()->setRGB($rgb);
2388
                                unset($fill->startcolorIndex); // normal color index does not apply, discard
2389
                            }
2390
                        }
2391
                        break;
2392 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...
2393
                        $xclfType  = self::getInt2d($extData, 0); // color type
2394
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2395
2396
                        if ($xclfType == 2) {
2397
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2398
2399
                            // modify the relevant style property
2400
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2401
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2402
                                $fill->getEndColor()->setRGB($rgb);
2403
                                unset($fill->endcolorIndex); // normal color index does not apply, discard
2404
                            }
2405
                        }
2406
                        break;
2407 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...
2408
                        $xclfType  = self::getInt2d($extData, 0); // color type
2409
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2410
2411
                        if ($xclfType == 2) {
2412
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2413
2414
                            // modify the relevant style property
2415
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2416
                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2417
                                $top->getColor()->setRGB($rgb);
2418
                                unset($top->colorIndex); // normal color index does not apply, discard
2419
                            }
2420
                        }
2421
                        break;
2422 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...
2423
                        $xclfType  = self::getInt2d($extData, 0); // color type
2424
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2425
2426
                        if ($xclfType == 2) {
2427
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2428
2429
                            // modify the relevant style property
2430
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2431
                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2432
                                $bottom->getColor()->setRGB($rgb);
2433
                                unset($bottom->colorIndex); // normal color index does not apply, discard
2434
                            }
2435
                        }
2436
                        break;
2437 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...
2438
                        $xclfType  = self::getInt2d($extData, 0); // color type
2439
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2440
2441
                        if ($xclfType == 2) {
2442
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2443
2444
                            // modify the relevant style property
2445
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2446
                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2447
                                $left->getColor()->setRGB($rgb);
2448
                                unset($left->colorIndex); // normal color index does not apply, discard
2449
                            }
2450
                        }
2451
                        break;
2452 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...
2453
                        $xclfType  = self::getInt2d($extData, 0); // color type
2454
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2455
2456
                        if ($xclfType == 2) {
2457
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2458
2459
                            // modify the relevant style property
2460
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2461
                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2462
                                $right->getColor()->setRGB($rgb);
2463
                                unset($right->colorIndex); // normal color index does not apply, discard
2464
                            }
2465
                        }
2466
                        break;
2467 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...
2468
                        $xclfType  = self::getInt2d($extData, 0); // color type
2469
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2470
2471
                        if ($xclfType == 2) {
2472
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2473
2474
                            // modify the relevant style property
2475
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2476
                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2477
                                $diagonal->getColor()->setRGB($rgb);
2478
                                unset($diagonal->colorIndex); // normal color index does not apply, discard
2479
                            }
2480
                        }
2481
                        break;
2482 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...
2483
                        $xclfType  = self::getInt2d($extData, 0); // color type
2484
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2485
2486
                        if ($xclfType == 2) {
2487
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
2488
2489
                            // modify the relevant style property
2490
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2491
                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2492
                                $font->getColor()->setRGB($rgb);
2493
                                unset($font->colorIndex); // normal color index does not apply, discard
2494
                            }
2495
                        }
2496
                        break;
2497
                }
2498
2499
                $offset += $cb;
2500
            }
2501
        }
2502
    }
2503
2504
2505
    /**
2506
     * Read STYLE record
2507
     */
2508
    private function readStyle()
2509
    {
2510
        $length = self::getInt2d($this->data, $this->pos + 2);
2511
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2512
2513
        // move stream pointer to next record
2514
        $this->pos += 4 + $length;
2515
2516
        if (!$this->readDataOnly) {
2517
            // offset: 0; size: 2; index to XF record and flag for built-in style
2518
            $ixfe = self::getInt2d($recordData, 0);
2519
2520
            // bit: 11-0; mask 0x0FFF; index to XF record
2521
            $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...
2522
2523
            // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
2524
            $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
2525
2526
            if ($isBuiltIn) {
2527
                // offset: 2; size: 1; identifier for built-in style
2528
                $builtInId = ord($recordData{2});
2529
2530
                switch ($builtInId) {
2531
                    case 0x00:
2532
                        // currently, we are not using this for anything
2533
                        break;
2534
                    default:
2535
                        break;
2536
                }
2537
            } 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...
2538
                // user-defined; not supported by PhpSpreadsheet
2539
            }
2540
        }
2541
    }
2542
2543
2544
    /**
2545
     * Read PALETTE record
2546
     */
2547
    private function readPalette()
2548
    {
2549
        $length = self::getInt2d($this->data, $this->pos + 2);
2550
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2551
2552
        // move stream pointer to next record
2553
        $this->pos += 4 + $length;
2554
2555
        if (!$this->readDataOnly) {
2556
            // offset: 0; size: 2; number of following colors
2557
            $nm = self::getInt2d($recordData, 0);
2558
2559
            // list of RGB colors
2560
            for ($i = 0; $i < $nm; ++$i) {
2561
                $rgb = substr($recordData, 2 + 4 * $i, 4);
2562
                $this->palette[] = self::readRGB($rgb);
2563
            }
2564
        }
2565
    }
2566
2567
2568
    /**
2569
     * SHEET
2570
     *
2571
     * This record is  located in the  Workbook Globals
2572
     * Substream  and represents a sheet inside the workbook.
2573
     * One SHEET record is written for each sheet. It stores the
2574
     * sheet name and a stream offset to the BOF record of the
2575
     * respective Sheet Substream within the Workbook Stream.
2576
     *
2577
     * --    "OpenOffice.org's Documentation of the Microsoft
2578
     *         Excel File Format"
2579
     */
2580
    private function readSheet()
2581
    {
2582
        $length = self::getInt2d($this->data, $this->pos + 2);
2583
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2584
2585
        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
2586
        // NOTE: not encrypted
2587
        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
2588
2589
        // move stream pointer to next record
2590
        $this->pos += 4 + $length;
2591
2592
        // offset: 4; size: 1; sheet state
2593
        switch (ord($recordData{4})) {
2594
            case 0x00:
2595
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2596
                break;
2597
            case 0x01:
2598
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_HIDDEN;
2599
                break;
2600
            case 0x02:
2601
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VERYHIDDEN;
2602
                break;
2603
            default:
2604
                $sheetState = \PhpSpreadsheet\Worksheet::SHEETSTATE_VISIBLE;
2605
                break;
2606
        }
2607
2608
        // offset: 5; size: 1; sheet type
2609
        $sheetType = ord($recordData{5});
2610
2611
        // offset: 6; size: var; sheet name
2612 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...
2613
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2614
            $rec_name = $string['value'];
2615
        } elseif ($this->version == self::XLS_BIFF7) {
2616
            $string = $this->readByteStringShort(substr($recordData, 6));
2617
            $rec_name = $string['value'];
2618
        }
2619
2620
        $this->sheets[] = array(
2621
            '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...
2622
            'offset' => $rec_offset,
2623
            'sheetState' => $sheetState,
2624
            'sheetType' => $sheetType,
2625
        );
2626
    }
2627
2628
2629
    /**
2630
     * Read EXTERNALBOOK record
2631
     */
2632
    private function readExternalBook()
2633
    {
2634
        $length = self::getInt2d($this->data, $this->pos + 2);
2635
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2636
2637
        // move stream pointer to next record
2638
        $this->pos += 4 + $length;
2639
2640
        // offset within record data
2641
        $offset = 0;
2642
2643
        // there are 4 types of records
2644
        if (strlen($recordData) > 4) {
2645
            // external reference
2646
            // offset: 0; size: 2; number of sheet names ($nm)
2647
            $nm = self::getInt2d($recordData, 0);
2648
            $offset += 2;
2649
2650
            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
2651
            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2652
            $offset += $encodedUrlString['size'];
2653
2654
            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
2655
            $externalSheetNames = array();
2656
            for ($i = 0; $i < $nm; ++$i) {
2657
                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2658
                $externalSheetNames[] = $externalSheetNameString['value'];
2659
                $offset += $externalSheetNameString['size'];
2660
            }
2661
2662
            // store the record data
2663
            $this->externalBooks[] = array(
2664
                'type' => 'external',
2665
                'encodedUrl' => $encodedUrlString['value'],
2666
                'externalSheetNames' => $externalSheetNames,
2667
            );
2668
        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
2669
            // internal reference
2670
            // offset: 0; size: 2; number of sheet in this document
2671
            // 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...
2672
            $this->externalBooks[] = array(
2673
                'type' => 'internal',
2674
            );
2675
        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
2676
            // add-in function
2677
            // 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...
2678
            $this->externalBooks[] = array(
2679
                'type' => 'addInFunction',
2680
            );
2681
        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
2682
            // DDE links, OLE links
2683
            // 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...
2684
            // offset: 2; size: var; encoded source document name
2685
            $this->externalBooks[] = array(
2686
                'type' => 'DDEorOLE',
2687
            );
2688
        }
2689
    }
2690
2691
2692
    /**
2693
     * Read EXTERNNAME record.
2694
     */
2695
    private function readExternName()
2696
    {
2697
        $length = self::getInt2d($this->data, $this->pos + 2);
2698
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2699
2700
        // move stream pointer to next record
2701
        $this->pos += 4 + $length;
2702
2703
        // external sheet references provided for named cells
2704
        if ($this->version == self::XLS_BIFF8) {
2705
            // offset: 0; size: 2; options
2706
            $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...
2707
2708
            // 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...
2709
2710
            // offset: 4; size: 2; not used
2711
2712
            // offset: 6; size: var
2713
            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2714
2715
            // offset: var; size: var; formula data
2716
            $offset = 6 + $nameString['size'];
2717
            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
2718
2719
            $this->externalNames[] = array(
2720
                'name' => $nameString['value'],
2721
                'formula' => $formula,
2722
            );
2723
        }
2724
    }
2725
2726
2727
    /**
2728
     * Read EXTERNSHEET record
2729
     */
2730
    private function readExternSheet()
2731
    {
2732
        $length = self::getInt2d($this->data, $this->pos + 2);
2733
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2734
2735
        // move stream pointer to next record
2736
        $this->pos += 4 + $length;
2737
2738
        // external sheet references provided for named cells
2739
        if ($this->version == self::XLS_BIFF8) {
2740
            // offset: 0; size: 2; number of following ref structures
2741
            $nm = self::getInt2d($recordData, 0);
2742
            for ($i = 0; $i < $nm; ++$i) {
2743
                $this->ref[] = array(
2744
                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
2745
                    'externalBookIndex' => self::getInt2d($recordData, 2 + 6 * $i),
2746
                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
2747
                    'firstSheetIndex' => self::getInt2d($recordData, 4 + 6 * $i),
2748
                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
2749
                    'lastSheetIndex' => self::getInt2d($recordData, 6 + 6 * $i),
2750
                );
2751
            }
2752
        }
2753
    }
2754
2755
2756
    /**
2757
     * DEFINEDNAME
2758
     *
2759
     * This record is part of a Link Table. It contains the name
2760
     * and the token array of an internal defined name. Token
2761
     * arrays of defined names contain tokens with aberrant
2762
     * token classes.
2763
     *
2764
     * --    "OpenOffice.org's Documentation of the Microsoft
2765
     *         Excel File Format"
2766
     */
2767
    private function readDefinedName()
2768
    {
2769
        $length = self::getInt2d($this->data, $this->pos + 2);
2770
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2771
2772
        // move stream pointer to next record
2773
        $this->pos += 4 + $length;
2774
2775
        if ($this->version == self::XLS_BIFF8) {
2776
            // retrieves named cells
2777
2778
            // offset: 0; size: 2; option flags
2779
            $opts = self::getInt2d($recordData, 0);
2780
2781
            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
2782
            $isBuiltInName = (0x0020 & $opts) >> 5;
2783
2784
            // offset: 2; size: 1; keyboard shortcut
2785
2786
            // offset: 3; size: 1; length of the name (character count)
2787
            $nlen = ord($recordData{3});
2788
2789
            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
2790
            // note: there can also be additional data, this is not included in $flen
2791
            $flen = self::getInt2d($recordData, 4);
2792
2793
            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
2794
            $scope = self::getInt2d($recordData, 8);
2795
2796
            // offset: 14; size: var; Name (Unicode string without length field)
2797
            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2798
2799
            // offset: var; size: $flen; formula data
2800
            $offset = 14 + $string['size'];
2801
            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
2802
2803
            try {
2804
                $formula = $this->getFormulaFromStructure($formulaStructure);
2805
            } catch (\PhpSpreadsheet\Exception $e) {
2806
                $formula = '';
2807
            }
2808
2809
            $this->definedname[] = array(
2810
                'isBuiltInName' => $isBuiltInName,
2811
                'name' => $string['value'],
2812
                'formula' => $formula,
2813
                'scope' => $scope,
2814
            );
2815
        }
2816
    }
2817
2818
2819
    /**
2820
     * Read MSODRAWINGGROUP record
2821
     */
2822 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...
2823
    {
2824
        $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...
2825
2826
        // get spliced record data
2827
        $splicedRecordData = $this->getSplicedRecordData();
2828
        $recordData = $splicedRecordData['recordData'];
2829
2830
        $this->drawingGroupData .= $recordData;
2831
    }
2832
2833
2834
    /**
2835
     * SST - Shared String Table
2836
     *
2837
     * This record contains a list of all strings used anywhere
2838
     * in the workbook. Each string occurs only once. The
2839
     * workbook uses indexes into the list to reference the
2840
     * strings.
2841
     *
2842
     * --    "OpenOffice.org's Documentation of the Microsoft
2843
     *         Excel File Format"
2844
     **/
2845
    private function readSst()
2846
    {
2847
        // offset within (spliced) record data
2848
        $pos = 0;
2849
2850
        // get spliced record data
2851
        $splicedRecordData = $this->getSplicedRecordData();
2852
2853
        $recordData = $splicedRecordData['recordData'];
2854
        $spliceOffsets = $splicedRecordData['spliceOffsets'];
2855
2856
        // offset: 0; size: 4; total number of strings in the workbook
2857
        $pos += 4;
2858
2859
        // offset: 4; size: 4; number of following strings ($nm)
2860
        $nm = self::getInt4d($recordData, 4);
2861
        $pos += 4;
2862
2863
        // loop through the Unicode strings (16-bit length)
2864
        for ($i = 0; $i < $nm; ++$i) {
2865
            // number of characters in the Unicode string
2866
            $numChars = self::getInt2d($recordData, $pos);
2867
            $pos += 2;
2868
2869
            // option flags
2870
            $optionFlags = ord($recordData{$pos});
2871
            ++$pos;
2872
2873
            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
2874
            $isCompressed = (($optionFlags & 0x01) == 0) ;
2875
2876
            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
2877
            $hasAsian = (($optionFlags & 0x04) != 0);
2878
2879
            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
2880
            $hasRichText = (($optionFlags & 0x08) != 0);
2881
2882
            if ($hasRichText) {
2883
                // number of Rich-Text formatting runs
2884
                $formattingRuns = self::getInt2d($recordData, $pos);
2885
                $pos += 2;
2886
            }
2887
2888
            if ($hasAsian) {
2889
                // size of Asian phonetic setting
2890
                $extendedRunLength = self::getInt4d($recordData, $pos);
2891
                $pos += 4;
2892
            }
2893
2894
            // expected byte length of character array if not split
2895
            $len = ($isCompressed) ? $numChars : $numChars * 2;
2896
2897
            // look up limit position
2898
            foreach ($spliceOffsets as $spliceOffset) {
2899
                // it can happen that the string is empty, therefore we need
2900
                // <= and not just <
2901
                if ($pos <= $spliceOffset) {
2902
                    $limitpos = $spliceOffset;
2903
                    break;
2904
                }
2905
            }
2906
2907
            if ($pos + $len <= $limitpos) {
2908
                // character array is not split between records
2909
2910
                $retstr = substr($recordData, $pos, $len);
2911
                $pos += $len;
2912
            } else {
2913
                // character array is split between records
2914
2915
                // first part of character array
2916
                $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...
2917
2918
                $bytesRead = $limitpos - $pos;
2919
2920
                // remaining characters in Unicode string
2921
                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2922
2923
                $pos = $limitpos;
2924
2925
                // keep reading the characters
2926
                while ($charsLeft > 0) {
2927
                    // look up next limit position, in case the string span more than one continue record
2928
                    foreach ($spliceOffsets as $spliceOffset) {
2929
                        if ($pos < $spliceOffset) {
2930
                            $limitpos = $spliceOffset;
2931
                            break;
2932
                        }
2933
                    }
2934
2935
                    // repeated option flags
2936
                    // OpenOffice.org documentation 5.21
2937
                    $option = ord($recordData{$pos});
2938
                    ++$pos;
2939
2940
                    if ($isCompressed && ($option == 0)) {
2941
                        // 1st fragment compressed
2942
                        // this fragment compressed
2943
                        $len = min($charsLeft, $limitpos - $pos);
2944
                        $retstr .= substr($recordData, $pos, $len);
2945
                        $charsLeft -= $len;
2946
                        $isCompressed = true;
2947
                    } elseif (!$isCompressed && ($option != 0)) {
2948
                        // 1st fragment uncompressed
2949
                        // this fragment uncompressed
2950
                        $len = min($charsLeft * 2, $limitpos - $pos);
2951
                        $retstr .= substr($recordData, $pos, $len);
2952
                        $charsLeft -= $len / 2;
2953
                        $isCompressed = false;
2954
                    } elseif (!$isCompressed && ($option == 0)) {
2955
                        // 1st fragment uncompressed
2956
                        // this fragment compressed
2957
                        $len = min($charsLeft, $limitpos - $pos);
2958
                        for ($j = 0; $j < $len; ++$j) {
2959
                            $retstr .= $recordData{$pos + $j} . chr(0);
2960
                        }
2961
                        $charsLeft -= $len;
2962
                        $isCompressed = false;
2963
                    } else {
2964
                        // 1st fragment compressed
2965
                        // this fragment uncompressed
2966
                        $newstr = '';
2967
                        for ($j = 0; $j < strlen($retstr); ++$j) {
2968
                            $newstr .= $retstr[$j] . chr(0);
2969
                        }
2970
                        $retstr = $newstr;
2971
                        $len = min($charsLeft * 2, $limitpos - $pos);
2972
                        $retstr .= substr($recordData, $pos, $len);
2973
                        $charsLeft -= $len / 2;
2974
                        $isCompressed = false;
2975
                    }
2976
2977
                    $pos += $len;
2978
                }
2979
            }
2980
2981
            // convert to UTF-8
2982
            $retstr = self::encodeUTF16($retstr, $isCompressed);
2983
2984
            // read additional Rich-Text information, if any
2985
            $fmtRuns = array();
2986
            if ($hasRichText) {
2987
                // list of formatting runs
2988
                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...
2989
                    // first formatted character; zero-based
2990
                    $charPos = self::getInt2d($recordData, $pos + $j * 4);
2991
2992
                    // index to font record
2993
                    $fontIndex = self::getInt2d($recordData, $pos + 2 + $j * 4);
2994
2995
                    $fmtRuns[] = array(
2996
                        'charPos' => $charPos,
2997
                        'fontIndex' => $fontIndex,
2998
                    );
2999
                }
3000
                $pos += 4 * $formattingRuns;
3001
            }
3002
3003
            // read additional Asian phonetics information, if any
3004
            if ($hasAsian) {
3005
                // For Asian phonetic settings, we skip the extended string data
3006
                $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...
3007
            }
3008
3009
            // store the shared sting
3010
            $this->sst[] = array(
3011
                'value' => $retstr,
3012
                'fmtRuns' => $fmtRuns,
3013
            );
3014
        }
3015
3016
        // getSplicedRecordData() takes care of moving current position in data stream
3017
    }
3018
3019
3020
    /**
3021
     * Read PRINTGRIDLINES record
3022
     */
3023
    private function readPrintGridlines()
3024
    {
3025
        $length = self::getInt2d($this->data, $this->pos + 2);
3026
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3027
3028
        // move stream pointer to next record
3029
        $this->pos += 4 + $length;
3030
3031
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3032
            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
3033
            $printGridlines = (bool) self::getInt2d($recordData, 0);
3034
            $this->phpSheet->setPrintGridlines($printGridlines);
3035
        }
3036
    }
3037
3038
3039
    /**
3040
     * Read DEFAULTROWHEIGHT record
3041
     */
3042 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...
3043
    {
3044
        $length = self::getInt2d($this->data, $this->pos + 2);
3045
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3046
3047
        // move stream pointer to next record
3048
        $this->pos += 4 + $length;
3049
3050
        // offset: 0; size: 2; option flags
3051
        // 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...
3052
        $height = self::getInt2d($recordData, 2);
3053
        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3054
    }
3055
3056
3057
    /**
3058
     * Read SHEETPR record
3059
     */
3060
    private function readSheetPr()
3061
    {
3062
        $length = self::getInt2d($this->data, $this->pos + 2);
3063
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3064
3065
        // move stream pointer to next record
3066
        $this->pos += 4 + $length;
3067
3068
        // offset: 0; size: 2
3069
3070
        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
3071
        $isSummaryBelow = (0x0040 & self::getInt2d($recordData, 0)) >> 6;
3072
        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
3073
3074
        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
3075
        $isSummaryRight = (0x0080 & self::getInt2d($recordData, 0)) >> 7;
3076
        $this->phpSheet->setShowSummaryRight($isSummaryRight);
3077
3078
        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
3079
        // this corresponds to radio button setting in page setup dialog in Excel
3080
        $this->isFitToPages = (bool) ((0x0100 & self::getInt2d($recordData, 0)) >> 8);
3081
    }
3082
3083
3084
    /**
3085
     * Read HORIZONTALPAGEBREAKS record
3086
     */
3087 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...
3088
    {
3089
        $length = self::getInt2d($this->data, $this->pos + 2);
3090
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3091
3092
        // move stream pointer to next record
3093
        $this->pos += 4 + $length;
3094
3095
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3096
            // offset: 0; size: 2; number of the following row index structures
3097
            $nm = self::getInt2d($recordData, 0);
3098
3099
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3100
            for ($i = 0; $i < $nm; ++$i) {
3101
                $r = self::getInt2d($recordData, 2 + 6 * $i);
3102
                $cf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3103
                $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...
3104
3105
                // not sure why two column indexes are necessary?
3106
                $this->phpSheet->setBreakByColumnAndRow($cf, $r, \PhpSpreadsheet\Worksheet::BREAK_ROW);
3107
            }
3108
        }
3109
    }
3110
3111
3112
    /**
3113
     * Read VERTICALPAGEBREAKS record
3114
     */
3115 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...
3116
    {
3117
        $length = self::getInt2d($this->data, $this->pos + 2);
3118
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3119
3120
        // move stream pointer to next record
3121
        $this->pos += 4 + $length;
3122
3123
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3124
            // offset: 0; size: 2; number of the following column index structures
3125
            $nm = self::getInt2d($recordData, 0);
3126
3127
            // offset: 2; size: 6 * $nm; list of $nm row index structures
3128
            for ($i = 0; $i < $nm; ++$i) {
3129
                $c = self::getInt2d($recordData, 2 + 6 * $i);
3130
                $rf = self::getInt2d($recordData, 2 + 6 * $i + 2);
3131
                $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...
3132
3133
                // not sure why two row indexes are necessary?
3134
                $this->phpSheet->setBreakByColumnAndRow($c, $rf, \PhpSpreadsheet\Worksheet::BREAK_COLUMN);
3135
            }
3136
        }
3137
    }
3138
3139
3140
    /**
3141
     * Read HEADER record
3142
     */
3143 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...
3144
    {
3145
        $length = self::getInt2d($this->data, $this->pos + 2);
3146
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3147
3148
        // move stream pointer to next record
3149
        $this->pos += 4 + $length;
3150
3151
        if (!$this->readDataOnly) {
3152
            // offset: 0; size: var
3153
            // realized that $recordData can be empty even when record exists
3154
            if ($recordData) {
3155
                if ($this->version == self::XLS_BIFF8) {
3156
                    $string = self::readUnicodeStringLong($recordData);
3157
                } else {
3158
                    $string = $this->readByteStringShort($recordData);
3159
                }
3160
3161
                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
3162
                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
3163
            }
3164
        }
3165
    }
3166
3167
3168
    /**
3169
     * Read FOOTER record
3170
     */
3171 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...
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: var
3181
            // realized that $recordData can be empty even when record exists
3182
            if ($recordData) {
3183
                if ($this->version == self::XLS_BIFF8) {
3184
                    $string = self::readUnicodeStringLong($recordData);
3185
                } else {
3186
                    $string = $this->readByteStringShort($recordData);
3187
                }
3188
                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
3189
                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
3190
            }
3191
        }
3192
    }
3193
3194
3195
    /**
3196
     * Read HCENTER record
3197
     */
3198 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...
3199
    {
3200
        $length = self::getInt2d($this->data, $this->pos + 2);
3201
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3202
3203
        // move stream pointer to next record
3204
        $this->pos += 4 + $length;
3205
3206
        if (!$this->readDataOnly) {
3207
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3208
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3209
3210
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3211
        }
3212
    }
3213
3214
3215
    /**
3216
     * Read VCENTER record
3217
     */
3218 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...
3219
    {
3220
        $length = self::getInt2d($this->data, $this->pos + 2);
3221
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3222
3223
        // move stream pointer to next record
3224
        $this->pos += 4 + $length;
3225
3226
        if (!$this->readDataOnly) {
3227
            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
3228
            $isVerticalCentered = (bool) self::getInt2d($recordData, 0);
3229
3230
            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3231
        }
3232
    }
3233
3234
3235
    /**
3236
     * Read LEFTMARGIN record
3237
     */
3238
    private function readLeftMargin()
3239
    {
3240
        $length = self::getInt2d($this->data, $this->pos + 2);
3241
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3242
3243
        // move stream pointer to next record
3244
        $this->pos += 4 + $length;
3245
3246
        if (!$this->readDataOnly) {
3247
            // offset: 0; size: 8
3248
            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3249
        }
3250
    }
3251
3252
3253
    /**
3254
     * Read RIGHTMARGIN record
3255
     */
3256
    private function readRightMargin()
3257
    {
3258
        $length = self::getInt2d($this->data, $this->pos + 2);
3259
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3260
3261
        // move stream pointer to next record
3262
        $this->pos += 4 + $length;
3263
3264
        if (!$this->readDataOnly) {
3265
            // offset: 0; size: 8
3266
            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3267
        }
3268
    }
3269
3270
3271
    /**
3272
     * Read TOPMARGIN record
3273
     */
3274
    private function readTopMargin()
3275
    {
3276
        $length = self::getInt2d($this->data, $this->pos + 2);
3277
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3278
3279
        // move stream pointer to next record
3280
        $this->pos += 4 + $length;
3281
3282
        if (!$this->readDataOnly) {
3283
            // offset: 0; size: 8
3284
            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3285
        }
3286
    }
3287
3288
3289
    /**
3290
     * Read BOTTOMMARGIN record
3291
     */
3292
    private function readBottomMargin()
3293
    {
3294
        $length = self::getInt2d($this->data, $this->pos + 2);
3295
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3296
3297
        // move stream pointer to next record
3298
        $this->pos += 4 + $length;
3299
3300
        if (!$this->readDataOnly) {
3301
            // offset: 0; size: 8
3302
            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3303
        }
3304
    }
3305
3306
3307
    /**
3308
     * Read PAGESETUP record
3309
     */
3310
    private function readPageSetup()
3311
    {
3312
        $length = self::getInt2d($this->data, $this->pos + 2);
3313
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3314
3315
        // move stream pointer to next record
3316
        $this->pos += 4 + $length;
3317
3318
        if (!$this->readDataOnly) {
3319
            // offset: 0; size: 2; paper size
3320
            $paperSize = self::getInt2d($recordData, 0);
3321
3322
            // offset: 2; size: 2; scaling factor
3323
            $scale = self::getInt2d($recordData, 2);
3324
3325
            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
3326
            $fitToWidth = self::getInt2d($recordData, 6);
3327
3328
            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
3329
            $fitToHeight = self::getInt2d($recordData, 8);
3330
3331
            // offset: 10; size: 2; option flags
3332
3333
            // 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...
3334
            $isPortrait = (0x0002 & self::getInt2d($recordData, 10)) >> 1;
3335
3336
            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
3337
            // when this bit is set, do not use flags for those properties
3338
            $isNotInit = (0x0004 & self::getInt2d($recordData, 10)) >> 2;
3339
3340
            if (!$isNotInit) {
3341
                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3342
                switch ($isPortrait) {
3343
                    case 0:
3344
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
3345
                        break;
3346
                    case 1:
3347
                        $this->phpSheet->getPageSetup()->setOrientation(\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
3348
                        break;
3349
                }
3350
3351
                $this->phpSheet->getPageSetup()->setScale($scale, false);
3352
                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
3353
                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
3354
                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
3355
            }
3356
3357
            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
3358
            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3359
            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3360
3361
            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
3362
            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3363
            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3364
        }
3365
    }
3366
3367
3368
    /**
3369
     * PROTECT - Sheet protection (BIFF2 through BIFF8)
3370
     *   if this record is omitted, then it also means no sheet protection
3371
     */
3372 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...
3373
    {
3374
        $length = self::getInt2d($this->data, $this->pos + 2);
3375
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3376
3377
        // move stream pointer to next record
3378
        $this->pos += 4 + $length;
3379
3380
        if ($this->readDataOnly) {
3381
            return;
3382
        }
3383
3384
        // 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...
3385
3386
        // bit 0, mask 0x01; 1 = sheet is protected
3387
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3388
        $this->phpSheet->getProtection()->setSheet((bool)$bool);
3389
    }
3390
3391
3392
    /**
3393
     * SCENPROTECT
3394
     */
3395 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...
3396
    {
3397
        $length = self::getInt2d($this->data, $this->pos + 2);
3398
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3399
3400
        // move stream pointer to next record
3401
        $this->pos += 4 + $length;
3402
3403
        if ($this->readDataOnly) {
3404
            return;
3405
        }
3406
3407
        // 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...
3408
3409
        // bit: 0, mask 0x01; 1 = scenarios are protected
3410
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3411
3412
        $this->phpSheet->getProtection()->setScenarios((bool)$bool);
3413
    }
3414
3415
3416
    /**
3417
     * OBJECTPROTECT
3418
     */
3419 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...
3420
    {
3421
        $length = self::getInt2d($this->data, $this->pos + 2);
3422
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3423
3424
        // move stream pointer to next record
3425
        $this->pos += 4 + $length;
3426
3427
        if ($this->readDataOnly) {
3428
            return;
3429
        }
3430
3431
        // 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...
3432
3433
        // bit: 0, mask 0x01; 1 = objects are protected
3434
        $bool = (0x01 & self::getInt2d($recordData, 0)) >> 0;
3435
3436
        $this->phpSheet->getProtection()->setObjects((bool)$bool);
3437
    }
3438
3439
3440
    /**
3441
     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
3442
     */
3443
    private function readPassword()
3444
    {
3445
        $length = self::getInt2d($this->data, $this->pos + 2);
3446
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3447
3448
        // move stream pointer to next record
3449
        $this->pos += 4 + $length;
3450
3451
        if (!$this->readDataOnly) {
3452
            // offset: 0; size: 2; 16-bit hash value of password
3453
            $password = strtoupper(dechex(self::getInt2d($recordData, 0))); // the hashed password
3454
            $this->phpSheet->getProtection()->setPassword($password, true);
3455
        }
3456
    }
3457
3458
3459
    /**
3460
     * Read DEFCOLWIDTH record
3461
     */
3462 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...
3463
    {
3464
        $length = self::getInt2d($this->data, $this->pos + 2);
3465
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3466
3467
        // move stream pointer to next record
3468
        $this->pos += 4 + $length;
3469
3470
        // offset: 0; size: 2; default column width
3471
        $width = self::getInt2d($recordData, 0);
3472
        if ($width != 8) {
3473
            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3474
        }
3475
    }
3476
3477
3478
    /**
3479
     * Read COLINFO record
3480
     */
3481
    private function readColInfo()
3482
    {
3483
        $length = self::getInt2d($this->data, $this->pos + 2);
3484
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3485
3486
        // move stream pointer to next record
3487
        $this->pos += 4 + $length;
3488
3489
        if (!$this->readDataOnly) {
3490
            // offset: 0; size: 2; index to first column in range
3491
            $fc = self::getInt2d($recordData, 0); // first column index
3492
3493
            // offset: 2; size: 2; index to last column in range
3494
            $lc = self::getInt2d($recordData, 2); // first column index
3495
3496
            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
3497
            $width = self::getInt2d($recordData, 4);
3498
3499
            // offset: 6; size: 2; index to XF record for default column formatting
3500
            $xfIndex = self::getInt2d($recordData, 6);
3501
3502
            // offset: 8; size: 2; option flags
3503
            // bit: 0; mask: 0x0001; 1= columns are hidden
3504
            $isHidden = (0x0001 & self::getInt2d($recordData, 8)) >> 0;
3505
3506
            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
3507
            $level = (0x0700 & self::getInt2d($recordData, 8)) >> 8;
3508
3509
            // bit: 12; mask: 0x1000; 1 = collapsed
3510
            $isCollapsed = (0x1000 & self::getInt2d($recordData, 8)) >> 12;
3511
3512
            // offset: 10; size: 2; not used
3513
3514
            for ($i = $fc; $i <= $lc; ++$i) {
3515
                if ($lc == 255 || $lc == 256) {
3516
                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3517
                    break;
3518
                }
3519
                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
3520
                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
3521
                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
3522
                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
3523
                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3524
            }
3525
        }
3526
    }
3527
3528
3529
    /**
3530
     * ROW
3531
     *
3532
     * This record contains the properties of a single row in a
3533
     * sheet. Rows and cells in a sheet are divided into blocks
3534
     * of 32 rows.
3535
     *
3536
     * --    "OpenOffice.org's Documentation of the Microsoft
3537
     *         Excel File Format"
3538
     */
3539
    private function readRow()
3540
    {
3541
        $length = self::getInt2d($this->data, $this->pos + 2);
3542
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3543
3544
        // move stream pointer to next record
3545
        $this->pos += 4 + $length;
3546
3547
        if (!$this->readDataOnly) {
3548
            // offset: 0; size: 2; index of this row
3549
            $r = self::getInt2d($recordData, 0);
3550
3551
            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
3552
3553
            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
3554
3555
            // 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...
3556
3557
            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
3558
            $height = (0x7FFF & self::getInt2d($recordData, 6)) >> 0;
3559
3560
            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
3561
            $useDefaultHeight = (0x8000 & self::getInt2d($recordData, 6)) >> 15;
3562
3563
            if (!$useDefaultHeight) {
3564
                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
3565
            }
3566
3567
            // offset: 8; size: 2; not used
3568
3569
            // offset: 10; size: 2; not used in BIFF5-BIFF8
3570
3571
            // offset: 12; size: 4; option flags and default row formatting
3572
3573
            // bit: 2-0: mask: 0x00000007; outline level of the row
3574
            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3575
            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
3576
3577
            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
3578
            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
3579
            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
3580
3581
            // bit: 5; mask: 0x00000020; 1 = row is hidden
3582
            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3583
            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
3584
3585
            // bit: 7; mask: 0x00000080; 1 = row has explicit format
3586
            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3587
3588
            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
3589
            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3590
3591
            if ($hasExplicitFormat) {
3592
                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3593
            }
3594
        }
3595
    }
3596
3597
3598
    /**
3599
     * Read RK record
3600
     * This record represents a cell that contains an RK value
3601
     * (encoded integer or floating-point value). If a
3602
     * floating-point value cannot be encoded to an RK value,
3603
     * a NUMBER record will be written. This record replaces the
3604
     * record INTEGER written in BIFF2.
3605
     *
3606
     * --    "OpenOffice.org's Documentation of the Microsoft
3607
     *         Excel File Format"
3608
     */
3609 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...
3610
    {
3611
        $length = self::getInt2d($this->data, $this->pos + 2);
3612
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3613
3614
        // move stream pointer to next record
3615
        $this->pos += 4 + $length;
3616
3617
        // offset: 0; size: 2; index to row
3618
        $row = self::getInt2d($recordData, 0);
3619
3620
        // offset: 2; size: 2; index to column
3621
        $column = self::getInt2d($recordData, 2);
3622
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3623
3624
        // Read cell?
3625
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3626
            // offset: 4; size: 2; index to XF record
3627
            $xfIndex = self::getInt2d($recordData, 4);
3628
3629
            // offset: 6; size: 4; RK value
3630
            $rknum = self::getInt4d($recordData, 6);
3631
            $numValue = self::getIEEE754($rknum);
3632
3633
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3634
            if (!$this->readDataOnly) {
3635
                // add style information
3636
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3637
            }
3638
3639
            // add cell
3640
            $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3641
        }
3642
    }
3643
3644
3645
    /**
3646
     * Read LABELSST record
3647
     * This record represents a cell that contains a string. It
3648
     * replaces the LABEL record and RSTRING record used in
3649
     * BIFF2-BIFF5.
3650
     *
3651
     * --    "OpenOffice.org's Documentation of the Microsoft
3652
     *         Excel File Format"
3653
     */
3654
    private function readLabelSst()
3655
    {
3656
        $length = self::getInt2d($this->data, $this->pos + 2);
3657
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3658
3659
        // move stream pointer to next record
3660
        $this->pos += 4 + $length;
3661
3662
        // offset: 0; size: 2; index to row
3663
        $row = self::getInt2d($recordData, 0);
3664
3665
        // offset: 2; size: 2; index to column
3666
        $column = self::getInt2d($recordData, 2);
3667
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3668
3669
        $emptyCell = true;
3670
        // Read cell?
3671
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3672
            // offset: 4; size: 2; index to XF record
3673
            $xfIndex = self::getInt2d($recordData, 4);
3674
3675
            // offset: 6; size: 4; index to SST record
3676
            $index = self::getInt4d($recordData, 6);
3677
3678
            // add cell
3679
            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
3680
                // then we should treat as rich text
3681
                $richText = new \PhpSpreadsheet\RichText();
3682
                $charPos = 0;
3683
                $sstCount = count($this->sst[$index]['fmtRuns']);
3684
                for ($i = 0; $i <= $sstCount; ++$i) {
3685
                    if (isset($fmtRuns[$i])) {
3686
                        $text = \PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
3687
                        $charPos = $fmtRuns[$i]['charPos'];
3688
                    } else {
3689
                        $text = \PhpSpreadsheet\Shared\StringHelper::substring($this->sst[$index]['value'], $charPos, \PhpSpreadsheet\Shared\StringHelper::countCharacters($this->sst[$index]['value']));
3690
                    }
3691
3692
                    if (\PhpSpreadsheet\Shared\StringHelper::countCharacters($text) > 0) {
3693
                        if ($i == 0) { // first text run, no style
3694
                            $richText->createText($text);
3695
                        } else {
3696
                            $textRun = $richText->createTextRun($text);
3697
                            if (isset($fmtRuns[$i - 1])) {
3698
                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
3699
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
3700
                                } else {
3701
                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
3702
                                    // check the OpenOffice documentation of the FONT record
3703
                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
3704
                                }
3705
                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
3706
                            }
3707
                        }
3708
                    }
3709
                }
3710
                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
3711
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3712
                    $cell->setValueExplicit($richText, \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3713
                    $emptyCell = false;
3714
                }
3715
            } else {
3716
                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
3717
                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3718
                    $cell->setValueExplicit($this->sst[$index]['value'], \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
3719
                    $emptyCell = false;
3720
                }
3721
            }
3722
3723
            if (!$this->readDataOnly && !$emptyCell) {
3724
                // add style information
3725
                $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...
3726
            }
3727
        }
3728
    }
3729
3730
3731
    /**
3732
     * Read MULRK record
3733
     * This record represents a cell range containing RK value
3734
     * cells. All cells are located in the same row.
3735
     *
3736
     * --    "OpenOffice.org's Documentation of the Microsoft
3737
     *         Excel File Format"
3738
     */
3739
    private function readMulRk()
3740
    {
3741
        $length = self::getInt2d($this->data, $this->pos + 2);
3742
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3743
3744
        // move stream pointer to next record
3745
        $this->pos += 4 + $length;
3746
3747
        // offset: 0; size: 2; index to row
3748
        $row = self::getInt2d($recordData, 0);
3749
3750
        // offset: 2; size: 2; index to first column
3751
        $colFirst = self::getInt2d($recordData, 2);
3752
3753
        // offset: var; size: 2; index to last column
3754
        $colLast = self::getInt2d($recordData, $length - 2);
3755
        $columns = $colLast - $colFirst + 1;
3756
3757
        // offset within record data
3758
        $offset = 4;
3759
3760
        for ($i = 0; $i < $columns; ++$i) {
3761
            $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($colFirst + $i);
3762
3763
            // Read cell?
3764
            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3765
                // offset: var; size: 2; index to XF record
3766
                $xfIndex = self::getInt2d($recordData, $offset);
3767
3768
                // offset: var; size: 4; RK value
3769
                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3770
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3771
                if (!$this->readDataOnly) {
3772
                    // add style
3773
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3774
                }
3775
3776
                // add cell value
3777
                $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3778
            }
3779
3780
            $offset += 6;
3781
        }
3782
    }
3783
3784
3785
    /**
3786
     * Read NUMBER record
3787
     * This record represents a cell that contains a
3788
     * floating-point value.
3789
     *
3790
     * --    "OpenOffice.org's Documentation of the Microsoft
3791
     *         Excel File Format"
3792
     */
3793 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...
3794
    {
3795
        $length = self::getInt2d($this->data, $this->pos + 2);
3796
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3797
3798
        // move stream pointer to next record
3799
        $this->pos += 4 + $length;
3800
3801
        // offset: 0; size: 2; index to row
3802
        $row = self::getInt2d($recordData, 0);
3803
3804
        // offset: 2; size 2; index to column
3805
        $column = self::getInt2d($recordData, 2);
3806
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3807
3808
        // Read cell?
3809
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3810
            // offset 4; size: 2; index to XF record
3811
            $xfIndex = self::getInt2d($recordData, 4);
3812
3813
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3814
3815
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3816
            if (!$this->readDataOnly) {
3817
                // add cell style
3818
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3819
            }
3820
3821
            // add cell value
3822
            $cell->setValueExplicit($numValue, \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3823
        }
3824
    }
3825
3826
3827
    /**
3828
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3829
     * This record contains the token array and the result of a
3830
     * formula cell.
3831
     *
3832
     * --    "OpenOffice.org's Documentation of the Microsoft
3833
     *         Excel File Format"
3834
     */
3835
    private function readFormula()
3836
    {
3837
        $length = self::getInt2d($this->data, $this->pos + 2);
3838
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3839
3840
        // move stream pointer to next record
3841
        $this->pos += 4 + $length;
3842
3843
        // offset: 0; size: 2; row index
3844
        $row = self::getInt2d($recordData, 0);
3845
3846
        // offset: 2; size: 2; col index
3847
        $column = self::getInt2d($recordData, 2);
3848
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3849
3850
        // offset: 20: size: variable; formula structure
3851
        $formulaStructure = substr($recordData, 20);
3852
3853
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3854
        $options = self::getInt2d($recordData, 14);
3855
3856
        // bit: 0; mask: 0x0001; 1 = recalculate always
3857
        // bit: 1; mask: 0x0002; 1 = calculate on open
3858
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3859
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3860
3861
        // WARNING:
3862
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3863
        // the formula data may be ordinary formula data, therefore we need to check
3864
        // explicitly for the tExp token (0x01)
3865
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01;
3866
3867
        if ($isPartOfSharedFormula) {
3868
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3869
            // get the base cell, grab tExp token
3870
            $baseRow = self::getInt2d($formulaStructure, 3);
3871
            $baseCol = self::getInt2d($formulaStructure, 5);
3872
            $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...
3873
        }
3874
3875
        // Read cell?
3876
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3877
            if ($isPartOfSharedFormula) {
3878
                // formula is added to this cell after the sheet has been read
3879
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3880
            }
3881
3882
            // offset: 16: size: 4; not used
3883
3884
            // offset: 4; size: 2; XF index
3885
            $xfIndex = self::getInt2d($recordData, 4);
3886
3887
            // offset: 6; size: 8; result of the formula
3888
            if ((ord($recordData{6}) == 0) && (ord($recordData{12}) == 255) && (ord($recordData{13}) == 255)) {
3889
                // String formula. Result follows in appended STRING record
3890
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_STRING;
3891
3892
                // read possible SHAREDFMLA record
3893
                $code = self::getInt2d($this->data, $this->pos);
3894
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3895
                    $this->readSharedFmla();
3896
                }
3897
3898
                // read STRING record
3899
                $value = $this->readString();
3900 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...
3901
                && (ord($recordData{12}) == 255)
3902
                && (ord($recordData{13}) == 255)) {
3903
                // Boolean formula. Result is in +2; 0=false, 1=true
3904
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
3905
                $value = (bool) ord($recordData{8});
3906
            } elseif ((ord($recordData{6}) == 2)
3907
                && (ord($recordData{12}) == 255)
3908
                && (ord($recordData{13}) == 255)) {
3909
                // Error formula. Error code is in +2
3910
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
3911
                $value = Excel5\ErrorCode::lookup(ord($recordData{8}));
3912 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...
3913
                && (ord($recordData{12}) == 255)
3914
                && (ord($recordData{13}) == 255)) {
3915
                // Formula result is a null string
3916
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_NULL;
3917
                $value = '';
3918
            } else {
3919
                // forumla result is a number, first 14 bytes like _NUMBER record
3920
                $dataType = \PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
3921
                $value = self::extractNumber(substr($recordData, 6, 8));
3922
            }
3923
3924
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3925
            if (!$this->readDataOnly) {
3926
                // add cell style
3927
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3928
            }
3929
3930
            // store the formula
3931
            if (!$isPartOfSharedFormula) {
3932
                // not part of shared formula
3933
                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
3934
                try {
3935
                    if ($this->version != self::XLS_BIFF8) {
3936
                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
3937
                    }
3938
                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
3939
                    $cell->setValueExplicit('=' . $formula, \PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
3940
                } catch (\PhpSpreadsheet\Exception $e) {
3941
                    $cell->setValueExplicit($value, $dataType);
3942
                }
3943
            } else {
3944
                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...
3945
                    // do nothing at this point, formula id added later in the code
3946
                } else {
3947
                    $cell->setValueExplicit($value, $dataType);
3948
                }
3949
            }
3950
3951
            // store the cached calculated value
3952
            $cell->setCalculatedValue($value);
3953
        }
3954
    }
3955
3956
3957
    /**
3958
     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
3959
     * which usually contains relative references.
3960
     * These will be used to construct the formula in each shared formula part after the sheet is read.
3961
     */
3962
    private function readSharedFmla()
3963
    {
3964
        $length = self::getInt2d($this->data, $this->pos + 2);
3965
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3966
3967
        // move stream pointer to next record
3968
        $this->pos += 4 + $length;
3969
3970
        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
3971
        $cellRange = substr($recordData, 0, 6);
3972
        $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...
3973
3974
        // offset: 6, size: 1; not used
3975
3976
        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
3977
        $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...
3978
3979
        // offset: 8, size: var; Binary token array of the shared formula
3980
        $formula = substr($recordData, 8);
3981
3982
        // at this point we only store the shared formula for later use
3983
        $this->sharedFormulas[$this->_baseCell] = $formula;
3984
    }
3985
3986
3987
    /**
3988
     * Read a STRING record from current stream position and advance the stream pointer to next record
3989
     * This record is used for storing result from FORMULA record when it is a string, and
3990
     * it occurs directly after the FORMULA record
3991
     *
3992
     * @return string The string contents as UTF-8
3993
     */
3994
    private function readString()
3995
    {
3996
        $length = self::getInt2d($this->data, $this->pos + 2);
3997
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3998
3999
        // move stream pointer to next record
4000
        $this->pos += 4 + $length;
4001
4002
        if ($this->version == self::XLS_BIFF8) {
4003
            $string = self::readUnicodeStringLong($recordData);
4004
            $value = $string['value'];
4005
        } else {
4006
            $string = $this->readByteStringLong($recordData);
4007
            $value = $string['value'];
4008
        }
4009
4010
        return $value;
4011
    }
4012
4013
4014
    /**
4015
     * Read BOOLERR record
4016
     * This record represents a Boolean value or error value
4017
     * cell.
4018
     *
4019
     * --    "OpenOffice.org's Documentation of the Microsoft
4020
     *         Excel File Format"
4021
     */
4022
    private function readBoolErr()
4023
    {
4024
        $length = self::getInt2d($this->data, $this->pos + 2);
4025
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4026
4027
        // move stream pointer to next record
4028
        $this->pos += 4 + $length;
4029
4030
        // offset: 0; size: 2; row index
4031
        $row = self::getInt2d($recordData, 0);
4032
4033
        // offset: 2; size: 2; column index
4034
        $column = self::getInt2d($recordData, 2);
4035
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
4036
4037
        // Read cell?
4038
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4039
            // offset: 4; size: 2; index to XF record
4040
            $xfIndex = self::getInt2d($recordData, 4);
4041
4042
            // offset: 6; size: 1; the boolean value or error value
4043
            $boolErr = ord($recordData{6});
4044
4045
            // 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...
4046
            $isError = ord($recordData{7});
4047
4048
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4049
            switch ($isError) {
4050
                case 0: // boolean
4051
                    $value = (bool) $boolErr;
4052
4053
                    // add cell value
4054
                    $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_BOOL);
4055
                    break;
4056
                case 1: // error type
4057
                    $value = Excel5\ErrorCode::lookup($boolErr);
4058
4059
                    // add cell value
4060
                    $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_ERROR);
4061
                    break;
4062
            }
4063
4064
            if (!$this->readDataOnly) {
4065
                // add cell style
4066
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4067
            }
4068
        }
4069
    }
4070
4071
4072
    /**
4073
     * Read MULBLANK record
4074
     * This record represents a cell range of empty cells. All
4075
     * cells are located in the same row
4076
     *
4077
     * --    "OpenOffice.org's Documentation of the Microsoft
4078
     *         Excel File Format"
4079
     */
4080
    private function readMulBlank()
4081
    {
4082
        $length = self::getInt2d($this->data, $this->pos + 2);
4083
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4084
4085
        // move stream pointer to next record
4086
        $this->pos += 4 + $length;
4087
4088
        // offset: 0; size: 2; index to row
4089
        $row = self::getInt2d($recordData, 0);
4090
4091
        // offset: 2; size: 2; index to first column
4092
        $fc = self::getInt2d($recordData, 2);
4093
4094
        // offset: 4; size: 2 x nc; list of indexes to XF records
4095
        // add style information
4096
        if (!$this->readDataOnly && $this->readEmptyCells) {
4097
            for ($i = 0; $i < $length / 2 - 3; ++$i) {
4098
                $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc + $i);
4099
4100
                // Read cell?
4101
                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4102
                    $xfIndex = self::getInt2d($recordData, 4 + 2 * $i);
4103
                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4104
                }
4105
            }
4106
        }
4107
4108
        // offset: 6; size 2; index to last column (not needed)
4109
    }
4110
4111
4112
    /**
4113
     * Read LABEL record
4114
     * This record represents a cell that contains a string. In
4115
     * BIFF8 it is usually replaced by the LABELSST record.
4116
     * Excel still uses this record, if it copies unformatted
4117
     * text cells to the clipboard.
4118
     *
4119
     * --    "OpenOffice.org's Documentation of the Microsoft
4120
     *         Excel File Format"
4121
     */
4122
    private function readLabel()
4123
    {
4124
        $length = self::getInt2d($this->data, $this->pos + 2);
4125
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4126
4127
        // move stream pointer to next record
4128
        $this->pos += 4 + $length;
4129
4130
        // offset: 0; size: 2; index to row
4131
        $row = self::getInt2d($recordData, 0);
4132
4133
        // offset: 2; size: 2; index to column
4134
        $column = self::getInt2d($recordData, 2);
4135
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($column);
4136
4137
        // Read cell?
4138
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4139
            // offset: 4; size: 2; XF index
4140
            $xfIndex = self::getInt2d($recordData, 4);
4141
4142
            // add cell value
4143
            // todo: what if string is very long? continue record
4144 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...
4145
                $string = self::readUnicodeStringLong(substr($recordData, 6));
4146
                $value = $string['value'];
4147
            } else {
4148
                $string = $this->readByteStringLong(substr($recordData, 6));
4149
                $value = $string['value'];
4150
            }
4151
            if ($this->readEmptyCells || trim($value) !== '') {
4152
                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
4153
                $cell->setValueExplicit($value, \PhpSpreadsheet\Cell\DataType::TYPE_STRING);
4154
4155
                if (!$this->readDataOnly) {
4156
                    // add cell style
4157
                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4158
                }
4159
            }
4160
        }
4161
    }
4162
4163
4164
    /**
4165
     * Read BLANK record
4166
     */
4167
    private function readBlank()
4168
    {
4169
        $length = self::getInt2d($this->data, $this->pos + 2);
4170
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4171
4172
        // move stream pointer to next record
4173
        $this->pos += 4 + $length;
4174
4175
        // offset: 0; size: 2; row index
4176
        $row = self::getInt2d($recordData, 0);
4177
4178
        // offset: 2; size: 2; col index
4179
        $col = self::getInt2d($recordData, 2);
4180
        $columnString = \PhpSpreadsheet\Cell::stringFromColumnIndex($col);
4181
4182
        // Read cell?
4183
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
4184
            // offset: 4; size: 2; XF index
4185
            $xfIndex = self::getInt2d($recordData, 4);
4186
4187
            // add style information
4188
            if (!$this->readDataOnly && $this->readEmptyCells) {
4189
                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4190
            }
4191
        }
4192
    }
4193
4194
4195
    /**
4196
     * Read MSODRAWING record
4197
     */
4198 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...
4199
    {
4200
        $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...
4201
4202
        // get spliced record data
4203
        $splicedRecordData = $this->getSplicedRecordData();
4204
        $recordData = $splicedRecordData['recordData'];
4205
4206
        $this->drawingData .= $recordData;
4207
    }
4208
4209
4210
    /**
4211
     * Read OBJ record
4212
     */
4213
    private function readObj()
4214
    {
4215
        $length = self::getInt2d($this->data, $this->pos + 2);
4216
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4217
4218
        // move stream pointer to next record
4219
        $this->pos += 4 + $length;
4220
4221
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4222
            return;
4223
        }
4224
4225
        // recordData consists of an array of subrecords looking like this:
4226
        //    ft: 2 bytes; ftCmo type (0x15)
4227
        //    cb: 2 bytes; size in bytes of ftCmo data
4228
        //    ot: 2 bytes; Object Type
4229
        //    id: 2 bytes; Object id number
4230
        //    grbit: 2 bytes; Option Flags
4231
        //    data: var; subrecord data
4232
4233
        // for now, we are just interested in the second subrecord containing the object type
4234
        $ftCmoType  = self::getInt2d($recordData, 0);
4235
        $cbCmoSize  = self::getInt2d($recordData, 2);
4236
        $otObjType  = self::getInt2d($recordData, 4);
4237
        $idObjID    = self::getInt2d($recordData, 6);
4238
        $grbitOpts  = self::getInt2d($recordData, 6);
4239
4240
        $this->objs[] = array(
4241
            'ftCmoType' => $ftCmoType,
4242
            'cbCmoSize' => $cbCmoSize,
4243
            'otObjType' => $otObjType,
4244
            'idObjID'   => $idObjID,
4245
            'grbitOpts' => $grbitOpts
4246
        );
4247
        $this->textObjRef = $idObjID;
4248
4249
//        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...
4250
//        var_dump(end($this->objs));
4251
//        echo '<br />';
4252
    }
4253
4254
4255
    /**
4256
     * Read WINDOW2 record
4257
     */
4258
    private function readWindow2()
4259
    {
4260
        $length = self::getInt2d($this->data, $this->pos + 2);
4261
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4262
4263
        // move stream pointer to next record
4264
        $this->pos += 4 + $length;
4265
4266
        // offset: 0; size: 2; option flags
4267
        $options = self::getInt2d($recordData, 0);
4268
4269
        // offset: 2; size: 2; index to first visible row
4270
        $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...
4271
4272
        // offset: 4; size: 2; index to first visible colum
4273
        $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...
4274
        if ($this->version === self::XLS_BIFF8) {
4275
            // offset:  8; size: 2; not used
4276
            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
4277
            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
4278
            // offset: 14; size: 4; not used
4279
            $zoomscaleInPageBreakPreview = self::getInt2d($recordData, 10);
4280
            if ($zoomscaleInPageBreakPreview === 0) {
4281
                $zoomscaleInPageBreakPreview = 60;
4282
            }
4283
            $zoomscaleInNormalView = self::getInt2d($recordData, 12);
4284
            if ($zoomscaleInNormalView === 0) {
4285
                $zoomscaleInNormalView = 100;
4286
            }
4287
        }
4288
4289
        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
4290
        $showGridlines = (bool) ((0x0002 & $options) >> 1);
4291
        $this->phpSheet->setShowGridlines($showGridlines);
4292
4293
        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
4294
        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4295
        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4296
4297
        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
4298
        $this->frozen = (bool) ((0x0008 & $options) >> 3);
4299
4300
        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
4301
        $this->phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6));
4302
4303
        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
4304
        $isActive = (bool) ((0x0400 & $options) >> 10);
4305
        if ($isActive) {
4306
            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4307
        }
4308
4309
        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
4310
        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4311
4312
        //FIXME: set $firstVisibleRow and $firstVisibleColumn
4313
4314
        if ($this->phpSheet->getSheetView()->getView() !== \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT) {
4315
            //NOTE: this setting is inferior to page layout view(Excel2007-)
4316
            $view = $isPageBreakPreview ? \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : \PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_NORMAL;
4317
            $this->phpSheet->getSheetView()->setView($view);
4318
            if ($this->version === self::XLS_BIFF8) {
4319
                $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...
4320
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4321
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4322
            }
4323
        }
4324
    }
4325
4326
    /**
4327
     * Read PLV Record(Created by Excel2007 or upper)
4328
     */
4329
    private function readPageLayoutView()
4330
    {
4331
        $length = self::getInt2d($this->data, $this->pos + 2);
4332
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4333
4334
        // move stream pointer to next record
4335
        $this->pos += 4 + $length;
4336
4337
        //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...
4338
4339
        // offset: 0; size: 2; rt
4340
        //->ignore
4341
        $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...
4342
        // offset: 2; size: 2; grbitfr
4343
        //->ignore
4344
        $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...
4345
        // offset: 4; size: 8; reserved
4346
        //->ignore
4347
4348
        // offset: 12; size 2; zoom scale
4349
        $wScalePLV = self::getInt2d($recordData, 12);
4350
        // offset: 14; size 2; grbit
4351
        $grbit = self::getInt2d($recordData, 14);
4352
4353
        // decomprise grbit
4354
        $fPageLayoutView   = $grbit & 0x01;
4355
        $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...
4356
        $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...
4357
4358
        if ($fPageLayoutView === 1) {
4359
            $this->phpSheet->getSheetView()->setView(\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT);
4360
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4361
        }
4362
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4363
    }
4364
4365
    /**
4366
     * Read SCL record
4367
     */
4368
    private function readScl()
4369
    {
4370
        $length = self::getInt2d($this->data, $this->pos + 2);
4371
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4372
4373
        // move stream pointer to next record
4374
        $this->pos += 4 + $length;
4375
4376
        // offset: 0; size: 2; numerator of the view magnification
4377
        $numerator = self::getInt2d($recordData, 0);
4378
4379
        // offset: 2; size: 2; numerator of the view magnification
4380
        $denumerator = self::getInt2d($recordData, 2);
4381
4382
        // set the zoom scale (in percent)
4383
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4384
    }
4385
4386
4387
    /**
4388
     * Read PANE record
4389
     */
4390
    private function readPane()
4391
    {
4392
        $length = self::getInt2d($this->data, $this->pos + 2);
4393
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4394
4395
        // move stream pointer to next record
4396
        $this->pos += 4 + $length;
4397
4398
        if (!$this->readDataOnly) {
4399
            // offset: 0; size: 2; position of vertical split
4400
            $px = self::getInt2d($recordData, 0);
4401
4402
            // offset: 2; size: 2; position of horizontal split
4403
            $py = self::getInt2d($recordData, 2);
4404
4405
            if ($this->frozen) {
4406
                // frozen panes
4407
                $this->phpSheet->freezePane(\PhpSpreadsheet\Cell::stringFromColumnIndex($px) . ($py + 1));
4408
            } 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...
4409
                // unfrozen panes; split windows; not supported by PhpSpreadsheet core
4410
            }
4411
        }
4412
    }
4413
4414
4415
    /**
4416
     * Read SELECTION record. There is one such record for each pane in the sheet.
4417
     */
4418
    private function readSelection()
4419
    {
4420
        $length = self::getInt2d($this->data, $this->pos + 2);
4421
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4422
4423
        // move stream pointer to next record
4424
        $this->pos += 4 + $length;
4425
4426
        if (!$this->readDataOnly) {
4427
            // offset: 0; size: 1; pane identifier
4428
            $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...
4429
4430
            // offset: 1; size: 2; index to row of the active cell
4431
            $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...
4432
4433
            // offset: 3; size: 2; index to column of the active cell
4434
            $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...
4435
4436
            // offset: 5; size: 2; index into the following cell range list to the
4437
            //  entry that contains the active cell
4438
            $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...
4439
4440
            // offset: 7; size: var; cell range address list containing all selected cell ranges
4441
            $data = substr($recordData, 7);
4442
            $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
4443
4444
            $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
4445
4446
            // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
4447
            if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
4448
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
4449
            }
4450
4451
            // first row '1' + last row '65536' indicates that full column is selected
4452
            if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
4453
                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
4454
            }
4455
4456
            // first column 'A' + last column 'IV' indicates that full row is selected
4457
            if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
4458
                $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
4459
            }
4460
4461
            $this->phpSheet->setSelectedCells($selectedCells);
4462
        }
4463
    }
4464
4465
4466
    private function includeCellRangeFiltered($cellRangeAddress)
4467
    {
4468
        $includeCellRange = true;
4469
        if ($this->getReadFilter() !== null) {
4470
            $includeCellRange = false;
4471
            $rangeBoundaries = \PhpSpreadsheet\Cell::getRangeBoundaries($cellRangeAddress);
4472
            $rangeBoundaries[1][0]++;
4473
            for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; $row++) {
4474
                for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; $column++) {
4475
                    if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
4476
                        $includeCellRange = true;
4477
                        break 2;
4478
                    }
4479
                }
4480
            }
4481
        }
4482
        return $includeCellRange;
4483
    }
4484
4485
4486
    /**
4487
     * MERGEDCELLS
4488
     *
4489
     * This record contains the addresses of merged cell ranges
4490
     * in the current sheet.
4491
     *
4492
     * --    "OpenOffice.org's Documentation of the Microsoft
4493
     *         Excel File Format"
4494
     */
4495
    private function readMergedCells()
4496
    {
4497
        $length = self::getInt2d($this->data, $this->pos + 2);
4498
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4499
4500
        // move stream pointer to next record
4501
        $this->pos += 4 + $length;
4502
4503
        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
4504
            $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
4505
            foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
4506
                if ((strpos($cellRangeAddress, ':') !== false) &&
4507
                    ($this->includeCellRangeFiltered($cellRangeAddress))) {
4508
                    $this->phpSheet->mergeCells($cellRangeAddress);
4509
                }
4510
            }
4511
        }
4512
    }
4513
4514
4515
    /**
4516
     * Read HYPERLINK record
4517
     */
4518
    private function readHyperLink()
4519
    {
4520
        $length = self::getInt2d($this->data, $this->pos + 2);
4521
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4522
4523
        // move stream pointer forward to next record
4524
        $this->pos += 4 + $length;
4525
4526
        if (!$this->readDataOnly) {
4527
            // offset: 0; size: 8; cell range address of all cells containing this hyperlink
4528
            try {
4529
                $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...
4530
            } catch (\PhpSpreadsheet\Exception $e) {
4531
                return;
4532
            }
4533
4534
            // offset: 8, size: 16; GUID of StdLink
4535
4536
            // offset: 24, size: 4; unknown value
4537
4538
            // offset: 28, size: 4; option flags
4539
            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
4540
            $isFileLinkOrUrl = (0x00000001 & self::getInt2d($recordData, 28)) >> 0;
4541
4542
            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
4543
            $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...
4544
4545
            // 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...
4546
            $hasDesc = (0x00000014 & self::getInt2d($recordData, 28)) >> 2;
4547
4548
            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
4549
            $hasText = (0x00000008 & self::getInt2d($recordData, 28)) >> 3;
4550
4551
            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
4552
            $hasFrame = (0x00000080 & self::getInt2d($recordData, 28)) >> 7;
4553
4554
            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
4555
            $isUNC = (0x00000100 & self::getInt2d($recordData, 28)) >> 8;
4556
4557
            // offset within record data
4558
            $offset = 32;
4559
4560
            if ($hasDesc) {
4561
                // offset: 32; size: var; character count of description text
4562
                $dl = self::getInt4d($recordData, 32);
4563
                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
4564
                $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...
4565
                $offset += 4 + 2 * $dl;
4566
            }
4567
            if ($hasFrame) {
4568
                $fl = self::getInt4d($recordData, $offset);
4569
                $offset += 4 + 2 * $fl;
4570
            }
4571
4572
            // detect type of hyperlink (there are 4 types)
4573
            $hyperlinkType = null;
4574
4575
            if ($isUNC) {
4576
                $hyperlinkType = 'UNC';
4577
            } elseif (!$isFileLinkOrUrl) {
4578
                $hyperlinkType = 'workbook';
4579
            } elseif (ord($recordData{$offset}) == 0x03) {
4580
                $hyperlinkType = 'local';
4581
            } elseif (ord($recordData{$offset}) == 0xE0) {
4582
                $hyperlinkType = 'URL';
4583
            }
4584
4585
            switch ($hyperlinkType) {
4586
                case 'URL':
4587
                    // section 5.58.2: Hyperlink containing a URL
4588
                    // e.g. http://example.org/index.php
4589
4590
                    // offset: var; size: 16; GUID of URL Moniker
4591
                    $offset += 16;
4592
                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
4593
                    $us = self::getInt4d($recordData, $offset);
4594
                    $offset += 4;
4595
                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
4596
                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
4597
                    $nullOffset = strpos($url, 0x00);
4598
                    if ($nullOffset) {
4599
                        $url = substr($url, 0, $nullOffset);
4600
                    }
4601
                    $url .= $hasText ? '#' : '';
4602
                    $offset += $us;
4603
                    break;
4604
                case 'local':
4605
                    // section 5.58.3: Hyperlink to local file
4606
                    // examples:
4607
                    //   mydoc.txt
4608
                    //   ../../somedoc.xls#Sheet!A1
4609
4610
                    // offset: var; size: 16; GUI of File Moniker
4611
                    $offset += 16;
4612
4613
                    // offset: var; size: 2; directory up-level count.
4614
                    $upLevelCount = self::getInt2d($recordData, $offset);
4615
                    $offset += 2;
4616
4617
                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
4618
                    $sl = self::getInt4d($recordData, $offset);
4619
                    $offset += 4;
4620
4621
                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
4622
                    $shortenedFilePath = substr($recordData, $offset, $sl);
4623
                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
4624
                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
4625
4626
                    $offset += $sl;
4627
4628
                    // offset: var; size: 24; unknown sequence
4629
                    $offset += 24;
4630
4631
                    // extended file path
4632
                    // offset: var; size: 4; size of the following file link field including string lenth mark
4633
                    $sz = self::getInt4d($recordData, $offset);
4634
                    $offset += 4;
4635
4636
                    // only present if $sz > 0
4637
                    if ($sz > 0) {
4638
                        // offset: var; size: 4; size of the character array of the extended file path and name
4639
                        $xl = self::getInt4d($recordData, $offset);
4640
                        $offset += 4;
4641
4642
                        // offset: var; size 2; unknown
4643
                        $offset += 2;
4644
4645
                        // offset: var; size $xl; character array of the extended file path and name.
4646
                        $extendedFilePath = substr($recordData, $offset, $xl);
4647
                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
4648
                        $offset += $xl;
4649
                    }
4650
4651
                    // construct the path
4652
                    $url = str_repeat('..\\', $upLevelCount);
4653
                    $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...
4654
                    $url .= $hasText ? '#' : '';
4655
4656
                    break;
4657
                case 'UNC':
4658
                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
4659
                    // todo: implement
4660
                    return;
4661
                case 'workbook':
4662
                    // section 5.58.5: Hyperlink to the Current Workbook
4663
                    // e.g. Sheet2!B1:C2, stored in text mark field
4664
                    $url = 'sheet://';
4665
                    break;
4666
                default:
4667
                    return;
4668
            }
4669
4670
            if ($hasText) {
4671
                // offset: var; size: 4; character count of text mark including trailing zero word
4672
                $tl = self::getInt4d($recordData, $offset);
4673
                $offset += 4;
4674
                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
4675
                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
4676
                $url .= $text;
4677
            }
4678
4679
            // apply the hyperlink to all the relevant cells
4680
            foreach (\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
4681
                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
4682
            }
4683
        }
4684
    }
4685
4686
4687
    /**
4688
     * Read DATAVALIDATIONS record
4689
     */
4690
    private function readDataValidations()
4691
    {
4692
        $length = self::getInt2d($this->data, $this->pos + 2);
4693
        $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...
4694
4695
        // move stream pointer forward to next record
4696
        $this->pos += 4 + $length;
4697
    }
4698
4699
4700
    /**
4701
     * Read DATAVALIDATION record
4702
     */
4703
    private function readDataValidation()
4704
    {
4705
        $length = self::getInt2d($this->data, $this->pos + 2);
4706
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4707
4708
        // move stream pointer forward to next record
4709
        $this->pos += 4 + $length;
4710
4711
        if ($this->readDataOnly) {
4712
            return;
4713
        }
4714
4715
        // offset: 0; size: 4; Options
4716
        $options = self::getInt4d($recordData, 0);
4717
4718
        // 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...
4719
        $type = (0x0000000F & $options) >> 0;
4720 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...
4721
            case 0x00:
4722
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_NONE;
4723
                break;
4724
            case 0x01:
4725
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE;
4726
                break;
4727
            case 0x02:
4728
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_DECIMAL;
4729
                break;
4730
            case 0x03:
4731
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_LIST;
4732
                break;
4733
            case 0x04:
4734
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_DATE;
4735
                break;
4736
            case 0x05:
4737
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_TIME;
4738
                break;
4739
            case 0x06:
4740
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_TEXTLENGTH;
4741
                break;
4742
            case 0x07:
4743
                $type = \PhpSpreadsheet\Cell\DataValidation::TYPE_CUSTOM;
4744
                break;
4745
        }
4746
4747
        // bit: 4-6; mask: 0x00000070; error type
4748
        $errorStyle = (0x00000070 & $options) >> 4;
4749 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...
4750
            case 0x00:
4751
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_STOP;
4752
                break;
4753
            case 0x01:
4754
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_WARNING;
4755
                break;
4756
            case 0x02:
4757
                $errorStyle = \PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION;
4758
                break;
4759
        }
4760
4761
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
4762
        // I have only seen cases where this is 1
4763
        $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...
4764
4765
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
4766
        $allowBlank = (0x00000100 & $options) >> 8;
4767
4768
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
4769
        $suppressDropDown = (0x00000200 & $options) >> 9;
4770
4771
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
4772
        $showInputMessage = (0x00040000 & $options) >> 18;
4773
4774
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
4775
        $showErrorMessage = (0x00080000 & $options) >> 19;
4776
4777
        // bit: 20-23; mask: 0x00F00000; condition operator
4778
        $operator = (0x00F00000 & $options) >> 20;
4779 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...
4780
            case 0x00:
4781
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_BETWEEN;
4782
                break;
4783
            case 0x01:
4784
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTBETWEEN;
4785
                break;
4786
            case 0x02:
4787
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_EQUAL;
4788
                break;
4789
            case 0x03:
4790
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_NOTEQUAL;
4791
                break;
4792
            case 0x04:
4793
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHAN;
4794
                break;
4795
            case 0x05:
4796
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHAN;
4797
                break;
4798
            case 0x06:
4799
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_GREATERTHANOREQUAL;
4800
                break;
4801
            case 0x07:
4802
                $operator = \PhpSpreadsheet\Cell\DataValidation::OPERATOR_LESSTHANOREQUAL;
4803
                break;
4804
        }
4805
4806
        // offset: 4; size: var; title of the prompt box
4807
        $offset = 4;
4808
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4809
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4810
        $offset += $string['size'];
4811
4812
        // offset: var; size: var; title of the error box
4813
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4814
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
4815
        $offset += $string['size'];
4816
4817
        // offset: var; size: var; text of the prompt box
4818
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4819
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
4820
        $offset += $string['size'];
4821
4822
        // offset: var; size: var; text of the error box
4823
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
4824
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
4825
        $offset += $string['size'];
4826
4827
        // offset: var; size: 2; size of the formula data for the first condition
4828
        $sz1 = self::getInt2d($recordData, $offset);
4829
        $offset += 2;
4830
4831
        // offset: var; size: 2; not used
4832
        $offset += 2;
4833
4834
        // offset: var; size: $sz1; formula data for first condition (without size field)
4835
        $formula1 = substr($recordData, $offset, $sz1);
4836
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
4837
        try {
4838
            $formula1 = $this->getFormulaFromStructure($formula1);
4839
4840
            // in list type validity, null characters are used as item separators
4841
            if ($type == \PhpSpreadsheet\Cell\DataValidation::TYPE_LIST) {
4842
                $formula1 = str_replace(chr(0), ',', $formula1);
4843
            }
4844
        } catch (\PhpSpreadsheet\Exception $e) {
4845
            return;
4846
        }
4847
        $offset += $sz1;
4848
4849
        // offset: var; size: 2; size of the formula data for the first condition
4850
        $sz2 = self::getInt2d($recordData, $offset);
4851
        $offset += 2;
4852
4853
        // offset: var; size: 2; not used
4854
        $offset += 2;
4855
4856
        // offset: var; size: $sz2; formula data for second condition (without size field)
4857
        $formula2 = substr($recordData, $offset, $sz2);
4858
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
4859
        try {
4860
            $formula2 = $this->getFormulaFromStructure($formula2);
4861
        } catch (\PhpSpreadsheet\Exception $e) {
4862
            return;
4863
        }
4864
        $offset += $sz2;
4865
4866
        // offset: var; size: var; cell range address list with
4867
        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
4868
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
4869
4870
        foreach ($cellRangeAddresses as $cellRange) {
4871
            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4872
            foreach (\PhpSpreadsheet\Cell::extractAllCellReferencesInRange($stRange) as $coordinate) {
4873
                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4874
                $objValidation->setType($type);
4875
                $objValidation->setErrorStyle($errorStyle);
4876
                $objValidation->setAllowBlank((bool)$allowBlank);
4877
                $objValidation->setShowInputMessage((bool)$showInputMessage);
4878
                $objValidation->setShowErrorMessage((bool)$showErrorMessage);
4879
                $objValidation->setShowDropDown(!$suppressDropDown);
4880
                $objValidation->setOperator($operator);
4881
                $objValidation->setErrorTitle($errorTitle);
4882
                $objValidation->setError($error);
4883
                $objValidation->setPromptTitle($promptTitle);
4884
                $objValidation->setPrompt($prompt);
4885
                $objValidation->setFormula1($formula1);
4886
                $objValidation->setFormula2($formula2);
4887
            }
4888
        }
4889
    }
4890
4891
    /**
4892
     * Read SHEETLAYOUT record. Stores sheet tab color information.
4893
     */
4894
    private function readSheetLayout()
4895
    {
4896
        $length = self::getInt2d($this->data, $this->pos + 2);
4897
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4898
4899
        // move stream pointer to next record
4900
        $this->pos += 4 + $length;
4901
4902
        // local pointer in record data
4903
        $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...
4904
4905
        if (!$this->readDataOnly) {
4906
            // offset: 0; size: 2; repeated record identifier 0x0862
4907
4908
            // offset: 2; size: 10; not used
4909
4910
            // offset: 12; size: 4; size of record data
4911
            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
4912
            $sz = self::getInt4d($recordData, 12);
4913
4914
            switch ($sz) {
4915
                case 0x14:
4916
                    // offset: 16; size: 2; color index for sheet tab
4917
                    $colorIndex = self::getInt2d($recordData, 16);
4918
                    $color = Excel5\Color::map($colorIndex, $this->palette, $this->version);
4919
                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
4920
                    break;
4921
                case 0x28:
4922
                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
4923
                    return;
4924
                    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...
4925
            }
4926
        }
4927
    }
4928
4929
4930
    /**
4931
     * Read SHEETPROTECTION record (FEATHEADR)
4932
     */
4933
    private function readSheetProtection()
4934
    {
4935
        $length = self::getInt2d($this->data, $this->pos + 2);
4936
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4937
4938
        // move stream pointer to next record
4939
        $this->pos += 4 + $length;
4940
4941
        if ($this->readDataOnly) {
4942
            return;
4943
        }
4944
4945
        // offset: 0; size: 2; repeated record header
4946
4947
        // offset: 2; size: 2; FRT cell reference flag (=0 currently)
4948
4949
        // offset: 4; size: 8; Currently not used and set to 0
4950
4951
        // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
4952
        $isf = self::getInt2d($recordData, 12);
4953
        if ($isf != 2) {
4954
            return;
4955
        }
4956
4957
        // offset: 14; size: 1; =1 since this is a feat header
4958
4959
        // offset: 15; size: 4; size of rgbHdrSData
4960
4961
        // rgbHdrSData, assume "Enhanced Protection"
4962
        // offset: 19; size: 2; option flags
4963
        $options = self::getInt2d($recordData, 19);
4964
4965
        // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
4966
        $bool = (0x0001 & $options) >> 0;
4967
        $this->phpSheet->getProtection()->setObjects(!$bool);
4968
4969
        // bit: 1; mask 0x0002; edit scenarios
4970
        $bool = (0x0002 & $options) >> 1;
4971
        $this->phpSheet->getProtection()->setScenarios(!$bool);
4972
4973
        // bit: 2; mask 0x0004; format cells
4974
        $bool = (0x0004 & $options) >> 2;
4975
        $this->phpSheet->getProtection()->setFormatCells(!$bool);
4976
4977
        // bit: 3; mask 0x0008; format columns
4978
        $bool = (0x0008 & $options) >> 3;
4979
        $this->phpSheet->getProtection()->setFormatColumns(!$bool);
4980
4981
        // bit: 4; mask 0x0010; format rows
4982
        $bool = (0x0010 & $options) >> 4;
4983
        $this->phpSheet->getProtection()->setFormatRows(!$bool);
4984
4985
        // bit: 5; mask 0x0020; insert columns
4986
        $bool = (0x0020 & $options) >> 5;
4987
        $this->phpSheet->getProtection()->setInsertColumns(!$bool);
4988
4989
        // bit: 6; mask 0x0040; insert rows
4990
        $bool = (0x0040 & $options) >> 6;
4991
        $this->phpSheet->getProtection()->setInsertRows(!$bool);
4992
4993
        // bit: 7; mask 0x0080; insert hyperlinks
4994
        $bool = (0x0080 & $options) >> 7;
4995
        $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
4996
4997
        // bit: 8; mask 0x0100; delete columns
4998
        $bool = (0x0100 & $options) >> 8;
4999
        $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
5000
5001
        // bit: 9; mask 0x0200; delete rows
5002
        $bool = (0x0200 & $options) >> 9;
5003
        $this->phpSheet->getProtection()->setDeleteRows(!$bool);
5004
5005
        // bit: 10; mask 0x0400; select locked cells
5006
        $bool = (0x0400 & $options) >> 10;
5007
        $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
5008
5009
        // bit: 11; mask 0x0800; sort cell range
5010
        $bool = (0x0800 & $options) >> 11;
5011
        $this->phpSheet->getProtection()->setSort(!$bool);
5012
5013
        // bit: 12; mask 0x1000; auto filter
5014
        $bool = (0x1000 & $options) >> 12;
5015
        $this->phpSheet->getProtection()->setAutoFilter(!$bool);
5016
5017
        // bit: 13; mask 0x2000; pivot tables
5018
        $bool = (0x2000 & $options) >> 13;
5019
        $this->phpSheet->getProtection()->setPivotTables(!$bool);
5020
5021
        // bit: 14; mask 0x4000; select unlocked cells
5022
        $bool = (0x4000 & $options) >> 14;
5023
        $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
5024
5025
        // offset: 21; size: 2; not used
5026
    }
5027
5028
5029
    /**
5030
     * Read RANGEPROTECTION record
5031
     * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
5032
     * where it is referred to as FEAT record
5033
     */
5034
    private function readRangeProtection()
5035
    {
5036
        $length = self::getInt2d($this->data, $this->pos + 2);
5037
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5038
5039
        // move stream pointer to next record
5040
        $this->pos += 4 + $length;
5041
5042
        // local pointer in record data
5043
        $offset = 0;
5044
5045
        if (!$this->readDataOnly) {
5046
            $offset += 12;
5047
5048
            // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
5049
            $isf = self::getInt2d($recordData, 12);
5050
            if ($isf != 2) {
5051
                // we only read FEAT records of type 2
5052
                return;
5053
            }
5054
            $offset += 2;
5055
5056
            $offset += 5;
5057
5058
            // offset: 19; size: 2; count of ref ranges this feature is on
5059
            $cref = self::getInt2d($recordData, 19);
5060
            $offset += 2;
5061
5062
            $offset += 6;
5063
5064
            // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
5065
            $cellRanges = array();
5066
            for ($i = 0; $i < $cref; ++$i) {
5067
                try {
5068
                    $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
5069
                } catch (\PhpSpreadsheet\Exception $e) {
5070
                    return;
5071
                }
5072
                $cellRanges[] = $cellRange;
5073
                $offset += 8;
5074
            }
5075
5076
            // offset: var; size: var; variable length of feature specific data
5077
            $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...
5078
            $offset += 4;
5079
5080
            // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
5081
            $wPassword = self::getInt4d($recordData, $offset);
5082
            $offset += 4;
5083
5084
            // Apply range protection to sheet
5085
            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...
5086
                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
5087
            }
5088
        }
5089
    }
5090
5091
5092
    /**
5093
     * Read IMDATA record
5094
     */
5095
    private function readImData()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
5096
    {
5097
        $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...
5098
5099
        // get spliced record data
5100
        $splicedRecordData = $this->getSplicedRecordData();
5101
        $recordData = $splicedRecordData['recordData'];
5102
5103
        // UNDER CONSTRUCTION
5104
5105
        // offset: 0; size: 2; image format
5106
        $cf = self::getInt2d($recordData, 0);
5107
5108
        // offset: 2; size: 2; environment from which the file was written
5109
        $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...
5110
5111
        // offset: 4; size: 4; length of the image data
5112
        $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...
5113
5114
        // offset: 8; size: var; image data
5115
        $iData = substr($recordData, 8);
5116
5117
        switch ($cf) {
5118
            case 0x09: // Windows bitmap format
5119
                // BITMAPCOREINFO
5120
                // 1. BITMAPCOREHEADER
5121
                // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
5122
                $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...
5123
    //            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...
5124
5125
                // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
5126
                $bcWidth = self::getInt2d($iData, 4);
5127
    //            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...
5128
5129
                // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
5130
                $bcHeight = self::getInt2d($iData, 6);
5131
    //            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...
5132
                $ih = imagecreatetruecolor($bcWidth, $bcHeight);
5133
5134
                // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
5135
5136
                // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
5137
                $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...
5138
    //            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...
5139
5140
                $rgbString = substr($iData, 12);
5141
                $rgbTriples = array();
5142
                while (strlen($rgbString) > 0) {
5143
                    $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
5144
                    $rgbString = substr($rgbString, 3);
5145
                }
5146
                $x = 0;
5147
                $y = 0;
5148
                foreach ($rgbTriples as $i => $rgbTriple) {
5149
                    $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
5150
                    imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
5151
                    $x = ($x + 1) % $bcWidth;
5152
                    $y = $y + floor(($x + 1) / $bcWidth);
5153
                }
5154
                //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...
5155
5156
                $drawing = new \PhpSpreadsheet\Worksheet\Drawing();
5157
                $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...
5158
                $drawing->setWorksheet($this->phpSheet);
5159
                break;
5160
            case 0x02: // Windows metafile or Macintosh PICT format
5161
            case 0x0e: // native format
5162
            default:
5163
                break;
5164
        }
5165
5166
        // getSplicedRecordData() takes care of moving current position in data stream
5167
    }
5168
5169
5170
    /**
5171
     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
5172
     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
5173
     * In this case, we must treat the CONTINUE record as a MSODRAWING record
5174
     */
5175
    private function readContinue()
5176
    {
5177
        $length = self::getInt2d($this->data, $this->pos + 2);
5178
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
5179
5180
        // check if we are reading drawing data
5181
        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
5182
        if ($this->drawingData == '') {
5183
            // move stream pointer to next record
5184
            $this->pos += 4 + $length;
5185
5186
            return;
5187
        }
5188
5189
        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
5190
        if ($length < 4) {
5191
            // move stream pointer to next record
5192
            $this->pos += 4 + $length;
5193
5194
            return;
5195
        }
5196
5197
        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
5198
        // look inside CONTINUE record to see if it looks like a part of an Escher stream
5199
        // we know that Escher stream may be split at least at
5200
        //        0xF003 MsofbtSpgrContainer
5201
        //        0xF004 MsofbtSpContainer
5202
        //        0xF00D MsofbtClientTextbox
5203
        $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more
5204
5205
        $splitPoint = self::getInt2d($recordData, 2);
5206
        if (in_array($splitPoint, $validSplitPoints)) {
5207
            // get spliced record data (and move pointer to next record)
5208
            $splicedRecordData = $this->getSplicedRecordData();
5209
            $this->drawingData .= $splicedRecordData['recordData'];
5210
5211
            return;
5212
        }
5213
5214
        // move stream pointer to next record
5215
        $this->pos += 4 + $length;
5216
    }
5217
5218
5219
    /**
5220
     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
5221
     * records are found. Splices the record data pieces and returns the combined string as if record data
5222
     * is in one piece.
5223
     * Moves to next current position in data stream to start of next record different from a CONtINUE record
5224
     *
5225
     * @return array
5226
     */
5227
    private function getSplicedRecordData()
5228
    {
5229
        $data = '';
5230
        $spliceOffsets = array();
5231
5232
        $i = 0;
5233
        $spliceOffsets[0] = 0;
5234
5235
        do {
5236
            ++$i;
5237
5238
            // offset: 0; size: 2; identifier
5239
            $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...
5240
            // offset: 2; size: 2; length
5241
            $length = self::getInt2d($this->data, $this->pos + 2);
5242
            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
5243
5244
            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
5245
5246
            $this->pos += 4 + $length;
5247
            $nextIdentifier = self::getInt2d($this->data, $this->pos);
5248
        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5249
5250
        $splicedData = array(
5251
            'recordData' => $data,
5252
            'spliceOffsets' => $spliceOffsets,
5253
        );
5254
5255
        return $splicedData;
5256
    }
5257
5258
5259
    /**
5260
     * Convert formula structure into human readable Excel formula like 'A3+A5*5'
5261
     *
5262
     * @param string $formulaStructure The complete binary data for the formula
5263
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5264
     * @return string Human readable formula
5265
     */
5266
    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
5267
    {
5268
        // offset: 0; size: 2; size of the following formula data
5269
        $sz = self::getInt2d($formulaStructure, 0);
5270
5271
        // offset: 2; size: sz
5272
        $formulaData = substr($formulaStructure, 2, $sz);
5273
5274
        // for debug: dump the formula data
5275
        //echo '<xmp>';
5276
        //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...
5277
        //echo 'the entire formula data: ';
5278
        //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...
5279
        //echo "\n----\n";
5280
5281
        // offset: 2 + sz; size: variable (optional)
5282
        if (strlen($formulaStructure) > 2 + $sz) {
5283
            $additionalData = substr($formulaStructure, 2 + $sz);
5284
5285
            // for debug: dump the additional data
5286
            //echo 'the entire additional data: ';
5287
            //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...
5288
            //echo "\n----\n";
5289
        } else {
5290
            $additionalData = '';
5291
        }
5292
5293
        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
5294
    }
5295
5296
5297
    /**
5298
     * Take formula data and additional data for formula and return human readable formula
5299
     *
5300
     * @param string $formulaData The binary data for the formula itself
5301
     * @param string $additionalData Additional binary data going with the formula
5302
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5303
     * @return string Human readable formula
5304
     */
5305
    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
5306
    {
5307
        // start parsing the formula data
5308
        $tokens = array();
5309
5310
        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...
5311
            $tokens[] = $token;
5312
            $formulaData = substr($formulaData, $token['size']);
5313
5314
            // for debug: dump the token
5315
            //var_dump($token);
5316
        }
5317
5318
        $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...
5319
5320
        return $formulaString;
5321
    }
5322
5323
5324
    /**
5325
     * Take array of tokens together with additional data for formula and return human readable formula
5326
     *
5327
     * @param array $tokens
5328
     * @param array $additionalData Additional binary data going with the formula
5329
     * @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...
5330
     * @return string Human readable formula
5331
     */
5332
    private function createFormulaFromTokens($tokens, $additionalData)
5333
    {
5334
        // empty formula?
5335
        if (empty($tokens)) {
5336
            return '';
5337
        }
5338
5339
        $formulaStrings = array();
5340
        foreach ($tokens as $token) {
5341
            // initialize spaces
5342
            $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
5343
            $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
5344
            $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
5345
            $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
5346
            $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
5347
            $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
5348
5349
            switch ($token['name']) {
5350
                case 'tAdd': // addition
5351
                case 'tConcat': // addition
5352
                case 'tDiv': // division
5353
                case 'tEQ': // equality
5354
                case 'tGE': // greater than or equal
5355
                case 'tGT': // greater than
5356
                case 'tIsect': // intersection
5357
                case 'tLE': // less than or equal
5358
                case 'tList': // less than or equal
5359
                case 'tLT': // less than
5360
                case 'tMul': // multiplication
5361
                case 'tNE': // multiplication
5362
                case 'tPower': // power
5363
                case 'tRange': // range
5364
                case 'tSub': // subtraction
5365
                    $op2 = array_pop($formulaStrings);
5366
                    $op1 = array_pop($formulaStrings);
5367
                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
5368
                    unset($space0, $space1);
5369
                    break;
5370
                case 'tUplus': // unary plus
5371 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...
5372
                    $op = array_pop($formulaStrings);
5373
                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
5374
                    unset($space0, $space1);
5375
                    break;
5376 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...
5377
                    $op = array_pop($formulaStrings);
5378
                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
5379
                    unset($space0, $space1);
5380
                    break;
5381
                case 'tAttrVolatile': // indicates volatile function
5382
                case 'tAttrIf':
5383
                case 'tAttrSkip':
5384
                case 'tAttrChoose':
5385
                    // token is only important for Excel formula evaluator
5386
                    // do nothing
5387
                    break;
5388
                case 'tAttrSpace': // space / carriage return
5389
                    // space will be used when next token arrives, do not alter formulaString stack
5390
                    switch ($token['data']['spacetype']) {
5391
                        case 'type0':
5392
                            $space0 = str_repeat(' ', $token['data']['spacecount']);
5393
                            break;
5394
                        case 'type1':
5395
                            $space1 = str_repeat("\n", $token['data']['spacecount']);
5396
                            break;
5397
                        case 'type2':
5398
                            $space2 = str_repeat(' ', $token['data']['spacecount']);
5399
                            break;
5400
                        case 'type3':
5401
                            $space3 = str_repeat("\n", $token['data']['spacecount']);
5402
                            break;
5403
                        case 'type4':
5404
                            $space4 = str_repeat(' ', $token['data']['spacecount']);
5405
                            break;
5406
                        case 'type5':
5407
                            $space5 = str_repeat("\n", $token['data']['spacecount']);
5408
                            break;
5409
                    }
5410
                    break;
5411
                case 'tAttrSum': // SUM function with one parameter
5412
                    $op = array_pop($formulaStrings);
5413
                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
5414
                    unset($space0, $space1);
5415
                    break;
5416
                case 'tFunc': // function with fixed number of arguments
5417
                case 'tFuncV': // function with variable number of arguments
5418
                    if ($token['data']['function'] != '') {
5419
                        // normal function
5420
                        $ops = array(); // array of operators
5421 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...
5422
                            $ops[] = array_pop($formulaStrings);
5423
                        }
5424
                        $ops = array_reverse($ops);
5425
                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")";
5426
                        unset($space0, $space1);
5427
                    } else {
5428
                        // add-in function
5429
                        $ops = array(); // array of operators
5430 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...
5431
                            $ops[] = array_pop($formulaStrings);
5432
                        }
5433
                        $ops = array_reverse($ops);
5434
                        $function = array_pop($formulaStrings);
5435
                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ")";
5436
                        unset($space0, $space1);
5437
                    }
5438
                    break;
5439
                case 'tParen': // parenthesis
5440
                    $expression = array_pop($formulaStrings);
5441
                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
5442
                    unset($space2, $space3, $space4, $space5);
5443
                    break;
5444
                case 'tArray': // array constant
5445
                    $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...
5446
                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
5447
                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
5448
                    unset($space0, $space1);
5449
                    break;
5450
                case 'tMemArea':
5451
                    // bite off chunk of additional data
5452
                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
0 ignored issues
show
Bug introduced by
It seems like $additionalData defined by parameter $additionalData on line 5332 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...
5453
                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
5454
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5455
                    unset($space0, $space1);
5456
                    break;
5457
                case 'tArea': // cell range address
5458
                case 'tBool': // boolean
5459
                case 'tErr': // error code
5460
                case 'tInt': // integer
5461
                case 'tMemErr':
5462
                case 'tMemFunc':
5463
                case 'tMissArg':
5464
                case 'tName':
5465
                case 'tNameX':
5466
                case 'tNum': // number
5467
                case 'tRef': // single cell reference
5468
                case 'tRef3d': // 3d cell reference
5469
                case 'tArea3d': // 3d cell range reference
5470
                case 'tRefN':
5471
                case 'tAreaN':
5472
                case 'tStr': // string
5473
                    $formulaStrings[] = "$space1$space0{$token['data']}";
5474
                    unset($space0, $space1);
5475
                    break;
5476
            }
5477
        }
5478
        $formulaString = $formulaStrings[0];
5479
5480
        // for debug: dump the human readable formula
5481
        //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...
5482
        //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...
5483
5484
        return $formulaString;
5485
    }
5486
5487
5488
    /**
5489
     * Fetch next token from binary formula data
5490
     *
5491
     * @param string Formula data
5492
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
5493
     * @return array
5494
     * @throws Exception
5495
     */
5496
    private function getNextToken($formulaData, $baseCell = 'A1')
5497
    {
5498
        // offset: 0; size: 1; token id
5499
        $id = ord($formulaData[0]); // token id
5500
        $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...
5501
5502
        switch ($id) {
5503
            case 0x03:
5504
                $name = 'tAdd';
5505
                $size = 1;
5506
                $data = '+';
5507
                break;
5508
            case 0x04:
5509
                $name = 'tSub';
5510
                $size = 1;
5511
                $data = '-';
5512
                break;
5513
            case 0x05:
5514
                $name = 'tMul';
5515
                $size = 1;
5516
                $data = '*';
5517
                break;
5518
            case 0x06:
5519
                $name = 'tDiv';
5520
                $size = 1;
5521
                $data = '/';
5522
                break;
5523
            case 0x07:
5524
                $name = 'tPower';
5525
                $size = 1;
5526
                $data = '^';
5527
                break;
5528
            case 0x08:
5529
                $name = 'tConcat';
5530
                $size = 1;
5531
                $data = '&';
5532
                break;
5533
            case 0x09:
5534
                $name = 'tLT';
5535
                $size = 1;
5536
                $data = '<';
5537
                break;
5538
            case 0x0A:
5539
                $name = 'tLE';
5540
                $size = 1;
5541
                $data = '<=';
5542
                break;
5543
            case 0x0B:
5544
                $name = 'tEQ';
5545
                $size = 1;
5546
                $data = '=';
5547
                break;
5548
            case 0x0C:
5549
                $name = 'tGE';
5550
                $size = 1;
5551
                $data = '>=';
5552
                break;
5553
            case 0x0D:
5554
                $name = 'tGT';
5555
                $size = 1;
5556
                $data = '>';
5557
                break;
5558
            case 0x0E:
5559
                $name = 'tNE';
5560
                $size = 1;
5561
                $data = '<>';
5562
                break;
5563
            case 0x0F:
5564
                $name = 'tIsect';
5565
                $size = 1;
5566
                $data = ' ';
5567
                break;
5568
            case 0x10:
5569
                $name = 'tList';
5570
                $size = 1;
5571
                $data = ',';
5572
                break;
5573
            case 0x11:
5574
                $name = 'tRange';
5575
                $size = 1;
5576
                $data = ':';
5577
                break;
5578
            case 0x12:
5579
                $name = 'tUplus';
5580
                $size = 1;
5581
                $data = '+';
5582
                break;
5583
            case 0x13:
5584
                $name = 'tUminus';
5585
                $size = 1;
5586
                $data = '-';
5587
                break;
5588
            case 0x14:
5589
                $name = 'tPercent';
5590
                $size = 1;
5591
                $data = '%';
5592
                break;
5593
            case 0x15:    //    parenthesis
5594
                $name  = 'tParen';
5595
                $size  = 1;
5596
                $data = null;
5597
                break;
5598
            case 0x16:    //    missing argument
5599
                $name = 'tMissArg';
5600
                $size = 1;
5601
                $data = '';
5602
                break;
5603
            case 0x17:    //    string
5604
                $name = 'tStr';
5605
                // offset: 1; size: var; Unicode string, 8-bit string length
5606
                $string = self::readUnicodeStringShort(substr($formulaData, 1));
5607
                $size = 1 + $string['size'];
5608
                $data = self::UTF8toExcelDoubleQuoted($string['value']);
5609
                break;
5610
            case 0x19:    //    Special attribute
5611
                // offset: 1; size: 1; attribute type flags:
5612
                switch (ord($formulaData[1])) {
5613
                    case 0x01:
5614
                        $name = 'tAttrVolatile';
5615
                        $size = 4;
5616
                        $data = null;
5617
                        break;
5618
                    case 0x02:
5619
                        $name = 'tAttrIf';
5620
                        $size = 4;
5621
                        $data = null;
5622
                        break;
5623
                    case 0x04:
5624
                        $name = 'tAttrChoose';
5625
                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
5626
                        $nc = self::getInt2d($formulaData, 2);
5627
                        // offset: 4; size: 2 * $nc
5628
                        // offset: 4 + 2 * $nc; size: 2
5629
                        $size = 2 * $nc + 6;
5630
                        $data = null;
5631
                        break;
5632
                    case 0x08:
5633
                        $name = 'tAttrSkip';
5634
                        $size = 4;
5635
                        $data = null;
5636
                        break;
5637
                    case 0x10:
5638
                        $name = 'tAttrSum';
5639
                        $size = 4;
5640
                        $data = null;
5641
                        break;
5642
                    case 0x40:
5643
                    case 0x41:
5644
                        $name = 'tAttrSpace';
5645
                        $size = 4;
5646
                        // offset: 2; size: 2; space type and position
5647
                        switch (ord($formulaData[2])) {
5648
                            case 0x00:
5649
                                $spacetype = 'type0';
5650
                                break;
5651
                            case 0x01:
5652
                                $spacetype = 'type1';
5653
                                break;
5654
                            case 0x02:
5655
                                $spacetype = 'type2';
5656
                                break;
5657
                            case 0x03:
5658
                                $spacetype = 'type3';
5659
                                break;
5660
                            case 0x04:
5661
                                $spacetype = 'type4';
5662
                                break;
5663
                            case 0x05:
5664
                                $spacetype = 'type5';
5665
                                break;
5666
                            default:
5667
                                throw new Exception('Unrecognized space type in tAttrSpace token');
5668
                                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...
5669
                        }
5670
                        // offset: 3; size: 1; number of inserted spaces/carriage returns
5671
                        $spacecount = ord($formulaData[3]);
5672
5673
                        $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount);
5674
                        break;
5675
                    default:
5676
                        throw new Exception('Unrecognized attribute flag in tAttr token');
5677
                        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...
5678
                }
5679
                break;
5680
            case 0x1C:    //    error code
5681
                // offset: 1; size: 1; error code
5682
                $name = 'tErr';
5683
                $size = 2;
5684
                $data = Excel5\ErrorCode::lookup(ord($formulaData[1]));
5685
                break;
5686
            case 0x1D:    //    boolean
5687
                // 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...
5688
                $name = 'tBool';
5689
                $size = 2;
5690
                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
5691
                break;
5692
            case 0x1E:    //    integer
5693
                // offset: 1; size: 2; unsigned 16-bit integer
5694
                $name = 'tInt';
5695
                $size = 3;
5696
                $data = self::getInt2d($formulaData, 1);
5697
                break;
5698
            case 0x1F:    //    number
5699
                // 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...
5700
                $name = 'tNum';
5701
                $size = 9;
5702
                $data = self::extractNumber(substr($formulaData, 1));
5703
                $data = str_replace(',', '.', (string)$data); // in case non-English locale
5704
                break;
5705
            case 0x20:    //    array constant
5706
            case 0x40:
5707
            case 0x60:
5708
                // offset: 1; size: 7; not used
5709
                $name = 'tArray';
5710
                $size = 8;
5711
                $data = null;
5712
                break;
5713
            case 0x21:    //    function with fixed number of arguments
5714
            case 0x41:
5715
            case 0x61:
5716
                $name = 'tFunc';
5717
                $size = 3;
5718
                // offset: 1; size: 2; index to built-in sheet function
5719
                switch (self::getInt2d($formulaData, 1)) {
5720
                    case 2:
5721
                        $function = 'ISNA';
5722
                        $args = 1;
5723
                        break;
5724
                    case 3:
5725
                        $function = 'ISERROR';
5726
                        $args = 1;
5727
                        break;
5728
                    case 10:
5729
                        $function = 'NA';
5730
                        $args = 0;
5731
                        break;
5732
                    case 15:
5733
                        $function = 'SIN';
5734
                        $args = 1;
5735
                        break;
5736
                    case 16:
5737
                        $function = 'COS';
5738
                        $args = 1;
5739
                        break;
5740
                    case 17:
5741
                        $function = 'TAN';
5742
                        $args = 1;
5743
                        break;
5744
                    case 18:
5745
                        $function = 'ATAN';
5746
                        $args = 1;
5747
                        break;
5748
                    case 19:
5749
                        $function = 'PI';
5750
                        $args = 0;
5751
                        break;
5752
                    case 20:
5753
                        $function = 'SQRT';
5754
                        $args = 1;
5755
                        break;
5756
                    case 21:
5757
                        $function = 'EXP';
5758
                        $args = 1;
5759
                        break;
5760
                    case 22:
5761
                        $function = 'LN';
5762
                        $args = 1;
5763
                        break;
5764
                    case 23:
5765
                        $function = 'LOG10';
5766
                        $args = 1;
5767
                        break;
5768
                    case 24:
5769
                        $function = 'ABS';
5770
                        $args = 1;
5771
                        break;
5772
                    case 25:
5773
                        $function = 'INT';
5774
                        $args = 1;
5775
                        break;
5776
                    case 26:
5777
                        $function = 'SIGN';
5778
                        $args = 1;
5779
                        break;
5780
                    case 27:
5781
                        $function = 'ROUND';
5782
                        $args = 2;
5783
                        break;
5784
                    case 30:
5785
                        $function = 'REPT';
5786
                        $args = 2;
5787
                        break;
5788
                    case 31:
5789
                        $function = 'MID';
5790
                        $args = 3;
5791
                        break;
5792
                    case 32:
5793
                        $function = 'LEN';
5794
                        $args = 1;
5795
                        break;
5796
                    case 33:
5797
                        $function = 'VALUE';
5798
                        $args = 1;
5799
                        break;
5800
                    case 34:
5801
                        $function = 'TRUE';
5802
                        $args = 0;
5803
                        break;
5804
                    case 35:
5805
                        $function = 'FALSE';
5806
                        $args = 0;
5807
                        break;
5808
                    case 38:
5809
                        $function = 'NOT';
5810
                        $args = 1;
5811
                        break;
5812
                    case 39:
5813
                        $function = 'MOD';
5814
                        $args = 2;
5815
                        break;
5816
                    case 40:
5817
                        $function = 'DCOUNT';
5818
                        $args = 3;
5819
                        break;
5820
                    case 41:
5821
                        $function = 'DSUM';
5822
                        $args = 3;
5823
                        break;
5824
                    case 42:
5825
                        $function = 'DAVERAGE';
5826
                        $args = 3;
5827
                        break;
5828
                    case 43:
5829
                        $function = 'DMIN';
5830
                        $args = 3;
5831
                        break;
5832
                    case 44:
5833
                        $function = 'DMAX';
5834
                        $args = 3;
5835
                        break;
5836
                    case 45:
5837
                        $function = 'DSTDEV';
5838
                        $args = 3;
5839
                        break;
5840
                    case 48:
5841
                        $function = 'TEXT';
5842
                        $args = 2;
5843
                        break;
5844
                    case 61:
5845
                        $function = 'MIRR';
5846
                        $args = 3;
5847
                        break;
5848
                    case 63:
5849
                        $function = 'RAND';
5850
                        $args = 0;
5851
                        break;
5852
                    case 65:
5853
                        $function = 'DATE';
5854
                        $args = 3;
5855
                        break;
5856
                    case 66:
5857
                        $function = 'TIME';
5858
                        $args = 3;
5859
                        break;
5860
                    case 67:
5861
                        $function = 'DAY';
5862
                        $args = 1;
5863
                        break;
5864
                    case 68:
5865
                        $function = 'MONTH';
5866
                        $args = 1;
5867
                        break;
5868
                    case 69:
5869
                        $function = 'YEAR';
5870
                        $args = 1;
5871
                        break;
5872
                    case 71:
5873
                        $function = 'HOUR';
5874
                        $args = 1;
5875
                        break;
5876
                    case 72:
5877
                        $function = 'MINUTE';
5878
                        $args = 1;
5879
                        break;
5880
                    case 73:
5881
                        $function = 'SECOND';
5882
                        $args = 1;
5883
                        break;
5884
                    case 74:
5885
                        $function = 'NOW';
5886
                        $args = 0;
5887
                        break;
5888
                    case 75:
5889
                        $function = 'AREAS';
5890
                        $args = 1;
5891
                        break;
5892
                    case 76:
5893
                        $function = 'ROWS';
5894
                        $args = 1;
5895
                        break;
5896
                    case 77:
5897
                        $function = 'COLUMNS';
5898
                        $args = 1;
5899
                        break;
5900
                    case 83:
5901
                        $function = 'TRANSPOSE';
5902
                        $args = 1;
5903
                        break;
5904
                    case 86:
5905
                        $function = 'TYPE';
5906
                        $args = 1;
5907
                        break;
5908
                    case 97:
5909
                        $function = 'ATAN2';
5910
                        $args = 2;
5911
                        break;
5912
                    case 98:
5913
                        $function = 'ASIN';
5914
                        $args = 1;
5915
                        break;
5916
                    case 99:
5917
                        $function = 'ACOS';
5918
                        $args = 1;
5919
                        break;
5920
                    case 105:
5921
                        $function = 'ISREF';
5922
                        $args = 1;
5923
                        break;
5924
                    case 111:
5925
                        $function = 'CHAR';
5926
                        $args = 1;
5927
                        break;
5928
                    case 112:
5929
                        $function = 'LOWER';
5930
                        $args = 1;
5931
                        break;
5932
                    case 113:
5933
                        $function = 'UPPER';
5934
                        $args = 1;
5935
                        break;
5936
                    case 114:
5937
                        $function = 'PROPER';
5938
                        $args = 1;
5939
                        break;
5940
                    case 117:
5941
                        $function = 'EXACT';
5942
                        $args = 2;
5943
                        break;
5944
                    case 118:
5945
                        $function = 'TRIM';
5946
                        $args = 1;
5947
                        break;
5948
                    case 119:
5949
                        $function = 'REPLACE';
5950
                        $args = 4;
5951
                        break;
5952
                    case 121:
5953
                        $function = 'CODE';
5954
                        $args = 1;
5955
                        break;
5956
                    case 126:
5957
                        $function = 'ISERR';
5958
                        $args = 1;
5959
                        break;
5960
                    case 127:
5961
                        $function = 'ISTEXT';
5962
                        $args = 1;
5963
                        break;
5964
                    case 128:
5965
                        $function = 'ISNUMBER';
5966
                        $args = 1;
5967
                        break;
5968
                    case 129:
5969
                        $function = 'ISBLANK';
5970
                        $args = 1;
5971
                        break;
5972
                    case 130:
5973
                        $function = 'T';
5974
                        $args = 1;
5975
                        break;
5976
                    case 131:
5977
                        $function = 'N';
5978
                        $args = 1;
5979
                        break;
5980
                    case 140:
5981
                        $function = 'DATEVALUE';
5982
                        $args = 1;
5983
                        break;
5984
                    case 141:
5985
                        $function = 'TIMEVALUE';
5986
                        $args = 1;
5987
                        break;
5988
                    case 142:
5989
                        $function = 'SLN';
5990
                        $args = 3;
5991
                        break;
5992
                    case 143:
5993
                        $function = 'SYD';
5994
                        $args = 4;
5995
                        break;
5996
                    case 162:
5997
                        $function = 'CLEAN';
5998
                        $args = 1;
5999
                        break;
6000
                    case 163:
6001
                        $function = 'MDETERM';
6002
                        $args = 1;
6003
                        break;
6004
                    case 164:
6005
                        $function = 'MINVERSE';
6006
                        $args = 1;
6007
                        break;
6008
                    case 165:
6009
                        $function = 'MMULT';
6010
                        $args = 2;
6011
                        break;
6012
                    case 184:
6013
                        $function = 'FACT';
6014
                        $args = 1;
6015
                        break;
6016
                    case 189:
6017
                        $function = 'DPRODUCT';
6018
                        $args = 3;
6019
                        break;
6020
                    case 190:
6021
                        $function = 'ISNONTEXT';
6022
                        $args = 1;
6023
                        break;
6024
                    case 195:
6025
                        $function = 'DSTDEVP';
6026
                        $args = 3;
6027
                        break;
6028
                    case 196:
6029
                        $function = 'DVARP';
6030
                        $args = 3;
6031
                        break;
6032
                    case 198:
6033
                        $function = 'ISLOGICAL';
6034
                        $args = 1;
6035
                        break;
6036
                    case 199:
6037
                        $function = 'DCOUNTA';
6038
                        $args = 3;
6039
                        break;
6040
                    case 207:
6041
                        $function = 'REPLACEB';
6042
                        $args = 4;
6043
                        break;
6044
                    case 210:
6045
                        $function = 'MIDB';
6046
                        $args = 3;
6047
                        break;
6048
                    case 211:
6049
                        $function = 'LENB';
6050
                        $args = 1;
6051
                        break;
6052
                    case 212:
6053
                        $function = 'ROUNDUP';
6054
                        $args = 2;
6055
                        break;
6056
                    case 213:
6057
                        $function = 'ROUNDDOWN';
6058
                        $args = 2;
6059
                        break;
6060
                    case 214:
6061
                        $function = 'ASC';
6062
                        $args = 1;
6063
                        break;
6064
                    case 215:
6065
                        $function = 'DBCS';
6066
                        $args = 1;
6067
                        break;
6068
                    case 221:
6069
                        $function = 'TODAY';
6070
                        $args = 0;
6071
                        break;
6072
                    case 229:
6073
                        $function = 'SINH';
6074
                        $args = 1;
6075
                        break;
6076
                    case 230:
6077
                        $function = 'COSH';
6078
                        $args = 1;
6079
                        break;
6080
                    case 231:
6081
                        $function = 'TANH';
6082
                        $args = 1;
6083
                        break;
6084
                    case 232:
6085
                        $function = 'ASINH';
6086
                        $args = 1;
6087
                        break;
6088
                    case 233:
6089
                        $function = 'ACOSH';
6090
                        $args = 1;
6091
                        break;
6092
                    case 234:
6093
                        $function = 'ATANH';
6094
                        $args = 1;
6095
                        break;
6096
                    case 235:
6097
                        $function = 'DGET';
6098
                        $args = 3;
6099
                        break;
6100
                    case 244:
6101
                        $function = 'INFO';
6102
                        $args = 1;
6103
                        break;
6104
                    case 252:
6105
                        $function = 'FREQUENCY';
6106
                        $args = 2;
6107
                        break;
6108
                    case 261:
6109
                        $function = 'ERROR.TYPE';
6110
                        $args = 1;
6111
                        break;
6112
                    case 271:
6113
                        $function = 'GAMMALN';
6114
                        $args = 1;
6115
                        break;
6116
                    case 273:
6117
                        $function = 'BINOMDIST';
6118
                        $args = 4;
6119
                        break;
6120
                    case 274:
6121
                        $function = 'CHIDIST';
6122
                        $args = 2;
6123
                        break;
6124
                    case 275:
6125
                        $function = 'CHIINV';
6126
                        $args = 2;
6127
                        break;
6128
                    case 276:
6129
                        $function = 'COMBIN';
6130
                        $args = 2;
6131
                        break;
6132
                    case 277:
6133
                        $function = 'CONFIDENCE';
6134
                        $args = 3;
6135
                        break;
6136
                    case 278:
6137
                        $function = 'CRITBINOM';
6138
                        $args = 3;
6139
                        break;
6140
                    case 279:
6141
                        $function = 'EVEN';
6142
                        $args = 1;
6143
                        break;
6144
                    case 280:
6145
                        $function = 'EXPONDIST';
6146
                        $args = 3;
6147
                        break;
6148
                    case 281:
6149
                        $function = 'FDIST';
6150
                        $args = 3;
6151
                        break;
6152
                    case 282:
6153
                        $function = 'FINV';
6154
                        $args = 3;
6155
                        break;
6156
                    case 283:
6157
                        $function = 'FISHER';
6158
                        $args = 1;
6159
                        break;
6160
                    case 284:
6161
                        $function = 'FISHERINV';
6162
                        $args = 1;
6163
                        break;
6164
                    case 285:
6165
                        $function = 'FLOOR';
6166
                        $args = 2;
6167
                        break;
6168
                    case 286:
6169
                        $function = 'GAMMADIST';
6170
                        $args = 4;
6171
                        break;
6172
                    case 287:
6173
                        $function = 'GAMMAINV';
6174
                        $args = 3;
6175
                        break;
6176
                    case 288:
6177
                        $function = 'CEILING';
6178
                        $args = 2;
6179
                        break;
6180
                    case 289:
6181
                        $function = 'HYPGEOMDIST';
6182
                        $args = 4;
6183
                        break;
6184
                    case 290:
6185
                        $function = 'LOGNORMDIST';
6186
                        $args = 3;
6187
                        break;
6188
                    case 291:
6189
                        $function = 'LOGINV';
6190
                        $args = 3;
6191
                        break;
6192
                    case 292:
6193
                        $function = 'NEGBINOMDIST';
6194
                        $args = 3;
6195
                        break;
6196
                    case 293:
6197
                        $function = 'NORMDIST';
6198
                        $args = 4;
6199
                        break;
6200
                    case 294:
6201
                        $function = 'NORMSDIST';
6202
                        $args = 1;
6203
                        break;
6204
                    case 295:
6205
                        $function = 'NORMINV';
6206
                        $args = 3;
6207
                        break;
6208
                    case 296:
6209
                        $function = 'NORMSINV';
6210
                        $args = 1;
6211
                        break;
6212
                    case 297:
6213
                        $function = 'STANDARDIZE';
6214
                        $args = 3;
6215
                        break;
6216
                    case 298:
6217
                        $function = 'ODD';
6218
                        $args = 1;
6219
                        break;
6220
                    case 299:
6221
                        $function = 'PERMUT';
6222
                        $args = 2;
6223
                        break;
6224
                    case 300:
6225
                        $function = 'POISSON';
6226
                        $args = 3;
6227
                        break;
6228
                    case 301:
6229
                        $function = 'TDIST';
6230
                        $args = 3;
6231
                        break;
6232
                    case 302:
6233
                        $function = 'WEIBULL';
6234
                        $args = 4;
6235
                        break;
6236
                    case 303:
6237
                        $function = 'SUMXMY2';
6238
                        $args = 2;
6239
                        break;
6240
                    case 304:
6241
                        $function = 'SUMX2MY2';
6242
                        $args = 2;
6243
                        break;
6244
                    case 305:
6245
                        $function = 'SUMX2PY2';
6246
                        $args = 2;
6247
                        break;
6248
                    case 306:
6249
                        $function = 'CHITEST';
6250
                        $args = 2;
6251
                        break;
6252
                    case 307:
6253
                        $function = 'CORREL';
6254
                        $args = 2;
6255
                        break;
6256
                    case 308:
6257
                        $function = 'COVAR';
6258
                        $args = 2;
6259
                        break;
6260
                    case 309:
6261
                        $function = 'FORECAST';
6262
                        $args = 3;
6263
                        break;
6264
                    case 310:
6265
                        $function = 'FTEST';
6266
                        $args = 2;
6267
                        break;
6268
                    case 311:
6269
                        $function = 'INTERCEPT';
6270
                        $args = 2;
6271
                        break;
6272
                    case 312:
6273
                        $function = 'PEARSON';
6274
                        $args = 2;
6275
                        break;
6276
                    case 313:
6277
                        $function = 'RSQ';
6278
                        $args = 2;
6279
                        break;
6280
                    case 314:
6281
                        $function = 'STEYX';
6282
                        $args = 2;
6283
                        break;
6284
                    case 315:
6285
                        $function = 'SLOPE';
6286
                        $args = 2;
6287
                        break;
6288
                    case 316:
6289
                        $function = 'TTEST';
6290
                        $args = 4;
6291
                        break;
6292
                    case 325:
6293
                        $function = 'LARGE';
6294
                        $args = 2;
6295
                        break;
6296
                    case 326:
6297
                        $function = 'SMALL';
6298
                        $args = 2;
6299
                        break;
6300
                    case 327:
6301
                        $function = 'QUARTILE';
6302
                        $args = 2;
6303
                        break;
6304
                    case 328:
6305
                        $function = 'PERCENTILE';
6306
                        $args = 2;
6307
                        break;
6308
                    case 331:
6309
                        $function = 'TRIMMEAN';
6310
                        $args = 2;
6311
                        break;
6312
                    case 332:
6313
                        $function = 'TINV';
6314
                        $args = 2;
6315
                        break;
6316
                    case 337:
6317
                        $function = 'POWER';
6318
                        $args = 2;
6319
                        break;
6320
                    case 342:
6321
                        $function = 'RADIANS';
6322
                        $args = 1;
6323
                        break;
6324
                    case 343:
6325
                        $function = 'DEGREES';
6326
                        $args = 1;
6327
                        break;
6328
                    case 346:
6329
                        $function = 'COUNTIF';
6330
                        $args = 2;
6331
                        break;
6332
                    case 347:
6333
                        $function = 'COUNTBLANK';
6334
                        $args = 1;
6335
                        break;
6336
                    case 350:
6337
                        $function = 'ISPMT';
6338
                        $args = 4;
6339
                        break;
6340
                    case 351:
6341
                        $function = 'DATEDIF';
6342
                        $args = 3;
6343
                        break;
6344
                    case 352:
6345
                        $function = 'DATESTRING';
6346
                        $args = 1;
6347
                        break;
6348
                    case 353:
6349
                        $function = 'NUMBERSTRING';
6350
                        $args = 2;
6351
                        break;
6352
                    case 360:
6353
                        $function = 'PHONETIC';
6354
                        $args = 1;
6355
                        break;
6356
                    case 368:
6357
                        $function = 'BAHTTEXT';
6358
                        $args = 1;
6359
                        break;
6360
                    default:
6361
                        throw new Exception('Unrecognized function in formula');
6362
                        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...
6363
                }
6364
                $data = array('function' => $function, 'args' => $args);
6365
                break;
6366
            case 0x22:    //    function with variable number of arguments
6367
            case 0x42:
6368
            case 0x62:
6369
                $name = 'tFuncV';
6370
                $size = 4;
6371
                // offset: 1; size: 1; number of arguments
6372
                $args = ord($formulaData[1]);
6373
                // offset: 2: size: 2; index to built-in sheet function
6374
                $index = self::getInt2d($formulaData, 2);
6375
                switch ($index) {
6376
                    case 0:
6377
                        $function = 'COUNT';
6378
                        break;
6379
                    case 1:
6380
                        $function = 'IF';
6381
                        break;
6382
                    case 4:
6383
                        $function = 'SUM';
6384
                        break;
6385
                    case 5:
6386
                        $function = 'AVERAGE';
6387
                        break;
6388
                    case 6:
6389
                        $function = 'MIN';
6390
                        break;
6391
                    case 7:
6392
                        $function = 'MAX';
6393
                        break;
6394
                    case 8:
6395
                        $function = 'ROW';
6396
                        break;
6397
                    case 9:
6398
                        $function = 'COLUMN';
6399
                        break;
6400
                    case 11:
6401
                        $function = 'NPV';
6402
                        break;
6403
                    case 12:
6404
                        $function = 'STDEV';
6405
                        break;
6406
                    case 13:
6407
                        $function = 'DOLLAR';
6408
                        break;
6409
                    case 14:
6410
                        $function = 'FIXED';
6411
                        break;
6412
                    case 28:
6413
                        $function = 'LOOKUP';
6414
                        break;
6415
                    case 29:
6416
                        $function = 'INDEX';
6417
                        break;
6418
                    case 36:
6419
                        $function = 'AND';
6420
                        break;
6421
                    case 37:
6422
                        $function = 'OR';
6423
                        break;
6424
                    case 46:
6425
                        $function = 'VAR';
6426
                        break;
6427
                    case 49:
6428
                        $function = 'LINEST';
6429
                        break;
6430
                    case 50:
6431
                        $function = 'TREND';
6432
                        break;
6433
                    case 51:
6434
                        $function = 'LOGEST';
6435
                        break;
6436
                    case 52:
6437
                        $function = 'GROWTH';
6438
                        break;
6439
                    case 56:
6440
                        $function = 'PV';
6441
                        break;
6442
                    case 57:
6443
                        $function = 'FV';
6444
                        break;
6445
                    case 58:
6446
                        $function = 'NPER';
6447
                        break;
6448
                    case 59:
6449
                        $function = 'PMT';
6450
                        break;
6451
                    case 60:
6452
                        $function = 'RATE';
6453
                        break;
6454
                    case 62:
6455
                        $function = 'IRR';
6456
                        break;
6457
                    case 64:
6458
                        $function = 'MATCH';
6459
                        break;
6460
                    case 70:
6461
                        $function = 'WEEKDAY';
6462
                        break;
6463
                    case 78:
6464
                        $function = 'OFFSET';
6465
                        break;
6466
                    case 82:
6467
                        $function = 'SEARCH';
6468
                        break;
6469
                    case 100:
6470
                        $function = 'CHOOSE';
6471
                        break;
6472
                    case 101:
6473
                        $function = 'HLOOKUP';
6474
                        break;
6475
                    case 102:
6476
                        $function = 'VLOOKUP';
6477
                        break;
6478
                    case 109:
6479
                        $function = 'LOG';
6480
                        break;
6481
                    case 115:
6482
                        $function = 'LEFT';
6483
                        break;
6484
                    case 116:
6485
                        $function = 'RIGHT';
6486
                        break;
6487
                    case 120:
6488
                        $function = 'SUBSTITUTE';
6489
                        break;
6490
                    case 124:
6491
                        $function = 'FIND';
6492
                        break;
6493
                    case 125:
6494
                        $function = 'CELL';
6495
                        break;
6496
                    case 144:
6497
                        $function = 'DDB';
6498
                        break;
6499
                    case 148:
6500
                        $function = 'INDIRECT';
6501
                        break;
6502
                    case 167:
6503
                        $function = 'IPMT';
6504
                        break;
6505
                    case 168:
6506
                        $function = 'PPMT';
6507
                        break;
6508
                    case 169:
6509
                        $function = 'COUNTA';
6510
                        break;
6511
                    case 183:
6512
                        $function = 'PRODUCT';
6513
                        break;
6514
                    case 193:
6515
                        $function = 'STDEVP';
6516
                        break;
6517
                    case 194:
6518
                        $function = 'VARP';
6519
                        break;
6520
                    case 197:
6521
                        $function = 'TRUNC';
6522
                        break;
6523
                    case 204:
6524
                        $function = 'USDOLLAR';
6525
                        break;
6526
                    case 205:
6527
                        $function = 'FINDB';
6528
                        break;
6529
                    case 206:
6530
                        $function = 'SEARCHB';
6531
                        break;
6532
                    case 208:
6533
                        $function = 'LEFTB';
6534
                        break;
6535
                    case 209:
6536
                        $function = 'RIGHTB';
6537
                        break;
6538
                    case 216:
6539
                        $function = 'RANK';
6540
                        break;
6541
                    case 219:
6542
                        $function = 'ADDRESS';
6543
                        break;
6544
                    case 220:
6545
                        $function = 'DAYS360';
6546
                        break;
6547
                    case 222:
6548
                        $function = 'VDB';
6549
                        break;
6550
                    case 227:
6551
                        $function = 'MEDIAN';
6552
                        break;
6553
                    case 228:
6554
                        $function = 'SUMPRODUCT';
6555
                        break;
6556
                    case 247:
6557
                        $function = 'DB';
6558
                        break;
6559
                    case 255:
6560
                        $function = '';
6561
                        break;
6562
                    case 269:
6563
                        $function = 'AVEDEV';
6564
                        break;
6565
                    case 270:
6566
                        $function = 'BETADIST';
6567
                        break;
6568
                    case 272:
6569
                        $function = 'BETAINV';
6570
                        break;
6571
                    case 317:
6572
                        $function = 'PROB';
6573
                        break;
6574
                    case 318:
6575
                        $function = 'DEVSQ';
6576
                        break;
6577
                    case 319:
6578
                        $function = 'GEOMEAN';
6579
                        break;
6580
                    case 320:
6581
                        $function = 'HARMEAN';
6582
                        break;
6583
                    case 321:
6584
                        $function = 'SUMSQ';
6585
                        break;
6586
                    case 322:
6587
                        $function = 'KURT';
6588
                        break;
6589
                    case 323:
6590
                        $function = 'SKEW';
6591
                        break;
6592
                    case 324:
6593
                        $function = 'ZTEST';
6594
                        break;
6595
                    case 329:
6596
                        $function = 'PERCENTRANK';
6597
                        break;
6598
                    case 330:
6599
                        $function = 'MODE';
6600
                        break;
6601
                    case 336:
6602
                        $function = 'CONCATENATE';
6603
                        break;
6604
                    case 344:
6605
                        $function = 'SUBTOTAL';
6606
                        break;
6607
                    case 345:
6608
                        $function = 'SUMIF';
6609
                        break;
6610
                    case 354:
6611
                        $function = 'ROMAN';
6612
                        break;
6613
                    case 358:
6614
                        $function = 'GETPIVOTDATA';
6615
                        break;
6616
                    case 359:
6617
                        $function = 'HYPERLINK';
6618
                        break;
6619
                    case 361:
6620
                        $function = 'AVERAGEA';
6621
                        break;
6622
                    case 362:
6623
                        $function = 'MAXA';
6624
                        break;
6625
                    case 363:
6626
                        $function = 'MINA';
6627
                        break;
6628
                    case 364:
6629
                        $function = 'STDEVPA';
6630
                        break;
6631
                    case 365:
6632
                        $function = 'VARPA';
6633
                        break;
6634
                    case 366:
6635
                        $function = 'STDEVA';
6636
                        break;
6637
                    case 367:
6638
                        $function = 'VARA';
6639
                        break;
6640
                    default:
6641
                        throw new Exception('Unrecognized function in formula');
6642
                        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...
6643
                }
6644
                $data = array('function' => $function, 'args' => $args);
6645
                break;
6646
            case 0x23:    //    index to defined name
6647
            case 0x43:
6648
            case 0x63:
6649
                $name = 'tName';
6650
                $size = 5;
6651
                // offset: 1; size: 2; one-based index to definedname record
6652
                $definedNameIndex = self::getInt2d($formulaData, 1) - 1;
6653
                // offset: 2; size: 2; not used
6654
                $data = $this->definedname[$definedNameIndex]['name'];
6655
                break;
6656
            case 0x24:    //    single cell reference e.g. A5
6657
            case 0x44:
6658
            case 0x64:
6659
                $name = 'tRef';
6660
                $size = 5;
6661
                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
6662
                break;
6663
            case 0x25:    //    cell range reference to cells in the same sheet (2d)
6664
            case 0x45:
6665
            case 0x65:
6666
                $name = 'tArea';
6667
                $size = 9;
6668
                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
6669
                break;
6670
            case 0x26:    //    Constant reference sub-expression
6671
            case 0x46:
6672 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...
6673
                $name = 'tMemArea';
6674
                // offset: 1; size: 4; not used
6675
                // offset: 5; size: 2; size of the following subexpression
6676
                $subSize = self::getInt2d($formulaData, 5);
6677
                $size = 7 + $subSize;
6678
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6679
                break;
6680
            case 0x27:    //    Deleted constant reference sub-expression
6681
            case 0x47:
6682 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...
6683
                $name = 'tMemErr';
6684
                // offset: 1; size: 4; not used
6685
                // offset: 5; size: 2; size of the following subexpression
6686
                $subSize = self::getInt2d($formulaData, 5);
6687
                $size = 7 + $subSize;
6688
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6689
                break;
6690
            case 0x29:    //    Variable reference sub-expression
6691
            case 0x49:
6692 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...
6693
                $name = 'tMemFunc';
6694
                // offset: 1; size: 2; size of the following sub-expression
6695
                $subSize = self::getInt2d($formulaData, 1);
6696
                $size = 3 + $subSize;
6697
                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
6698
                break;
6699
            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
6700
            case 0x4C:
6701
            case 0x6C:
6702
                $name = 'tRefN';
6703
                $size = 5;
6704
                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
6705
                break;
6706
            case 0x2D:    //    Relative 2d range reference
6707
            case 0x4D:
6708
            case 0x6D:
6709
                $name = 'tAreaN';
6710
                $size = 9;
6711
                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
6712
                break;
6713
            case 0x39:    //    External name
6714
            case 0x59:
6715
            case 0x79:
6716
                $name = 'tNameX';
6717
                $size = 7;
6718
                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
6719
                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
6720
                $index = self::getInt2d($formulaData, 3);
6721
                // assume index is to EXTERNNAME record
6722
                $data = $this->externalNames[$index - 1]['name'];
6723
                // offset: 5; size: 2; not used
6724
                break;
6725
            case 0x3A:    //    3d reference to cell
6726
            case 0x5A:
6727 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...
6728
                $name = 'tRef3d';
6729
                $size = 7;
6730
6731
                try {
6732
                    // offset: 1; size: 2; index to REF entry
6733
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6734
                    // offset: 3; size: 4; cell address
6735
                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
6736
6737
                    $data = "$sheetRange!$cellAddress";
6738
                } catch (\PhpSpreadsheet\Exception $e) {
6739
                    // deleted sheet reference
6740
                    $data = '#REF!';
6741
                }
6742
                break;
6743
            case 0x3B:    //    3d reference to cell range
6744
            case 0x5B:
6745 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...
6746
                $name = 'tArea3d';
6747
                $size = 11;
6748
6749
                try {
6750
                    // offset: 1; size: 2; index to REF entry
6751
                    $sheetRange = $this->readSheetRangeByRefIndex(self::getInt2d($formulaData, 1));
6752
                    // offset: 3; size: 8; cell address
6753
                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
6754
6755
                    $data = "$sheetRange!$cellRangeAddress";
6756
                } catch (\PhpSpreadsheet\Exception $e) {
6757
                    // deleted sheet reference
6758
                    $data = '#REF!';
6759
                }
6760
                break;
6761
            // Unknown cases    // don't know how to deal with
6762
            default:
6763
                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
6764
                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...
6765
        }
6766
6767
        return array(
6768
            'id' => $id,
6769
            'name' => $name,
6770
            'size' => $size,
6771
            'data' => $data,
6772
        );
6773
    }
6774
6775
6776
    /**
6777
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
6778
     * section 3.3.4
6779
     *
6780
     * @param string $cellAddressStructure
6781
     * @return string
6782
     */
6783
    private function readBIFF8CellAddress($cellAddressStructure)
6784
    {
6785
        // 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...
6786
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6787
6788
        // offset: 2; size: 2; index to column or column offset + relative flags
6789
        // bit: 7-0; mask 0x00FF; column index
6790
        $column = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($cellAddressStructure, 2));
6791
6792
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6793
        if (!(0x4000 & self::getInt2d($cellAddressStructure, 2))) {
6794
            $column = '$' . $column;
6795
        }
6796
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6797
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6798
            $row = '$' . $row;
6799
        }
6800
6801
        return $column . $row;
6802
    }
6803
6804
6805
    /**
6806
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6807
     * to indicate offsets from a base cell
6808
     * section 3.3.4
6809
     *
6810
     * @param string $cellAddressStructure
6811
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
6812
     * @return string
6813
     */
6814
    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
6815
    {
6816
        list($baseCol, $baseRow) = \PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6817
        $baseCol = \PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6818
6819
        // 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...
6820
        $rowIndex = self::getInt2d($cellAddressStructure, 0);
6821
        $row = self::getInt2d($cellAddressStructure, 0) + 1;
6822
6823
        // offset: 2; size: 2; index to column or column offset + relative flags
6824
        // bit: 7-0; mask 0x00FF; column index
6825
        $colIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
6826
6827
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6828 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...
6829
            $column = \PhpSpreadsheet\Cell::stringFromColumnIndex($colIndex);
6830
            $column = '$' . $column;
6831
        } else {
6832
            $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
6833
            $column = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $colIndex);
6834
        }
6835
6836
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6837
        if (!(0x8000 & self::getInt2d($cellAddressStructure, 2))) {
6838
            $row = '$' . $row;
6839
        } else {
6840
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
6841
            $row = $baseRow + $rowIndex;
6842
        }
6843
6844
        return $column . $row;
6845
    }
6846
6847
6848
    /**
6849
     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
6850
     * always fixed range
6851
     * section 2.5.14
6852
     *
6853
     * @param string $subData
6854
     * @return string
6855
     * @throws Exception
6856
     */
6857
    private function readBIFF5CellRangeAddressFixed($subData)
6858
    {
6859
        // offset: 0; size: 2; index to first row
6860
        $fr = self::getInt2d($subData, 0) + 1;
6861
6862
        // offset: 2; size: 2; index to last row
6863
        $lr = self::getInt2d($subData, 2) + 1;
6864
6865
        // offset: 4; size: 1; index to first column
6866
        $fc = ord($subData{4});
6867
6868
        // offset: 5; size: 1; index to last column
6869
        $lc = ord($subData{5});
6870
6871
        // check values
6872
        if ($fr > $lr || $fc > $lc) {
6873
            throw new Exception('Not a cell range address');
6874
        }
6875
6876
        // column index to letter
6877
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6878
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6879
6880
        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...
6881
            return "$fc$fr";
6882
        }
6883
        return "$fc$fr:$lc$lr";
6884
    }
6885
6886
6887
    /**
6888
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
6889
     * always fixed range
6890
     * section 2.5.14
6891
     *
6892
     * @param string $subData
6893
     * @return string
6894
     * @throws Exception
6895
     */
6896
    private function readBIFF8CellRangeAddressFixed($subData)
6897
    {
6898
        // offset: 0; size: 2; index to first row
6899
        $fr = self::getInt2d($subData, 0) + 1;
6900
6901
        // offset: 2; size: 2; index to last row
6902
        $lr = self::getInt2d($subData, 2) + 1;
6903
6904
        // offset: 4; size: 2; index to first column
6905
        $fc = self::getInt2d($subData, 4);
6906
6907
        // offset: 6; size: 2; index to last column
6908
        $lc = self::getInt2d($subData, 6);
6909
6910
        // check values
6911
        if ($fr > $lr || $fc > $lc) {
6912
            throw new Exception('Not a cell range address');
6913
        }
6914
6915
        // column index to letter
6916
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fc);
6917
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lc);
6918
6919
        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...
6920
            return "$fc$fr";
6921
        }
6922
        return "$fc$fr:$lc$lr";
6923
    }
6924
6925
6926
    /**
6927
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
6928
     * there are flags indicating whether column/row index is relative
6929
     * section 3.3.4
6930
     *
6931
     * @param string $subData
6932
     * @return string
6933
     */
6934
    private function readBIFF8CellRangeAddress($subData)
6935
    {
6936
        // todo: if cell range is just a single cell, should this funciton
6937
        // not just return e.g. 'A1' and not 'A1:A1' ?
6938
6939
        // 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...
6940
            $fr = self::getInt2d($subData, 0) + 1;
6941
6942
        // 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...
6943
            $lr = self::getInt2d($subData, 2) + 1;
6944
6945
        // offset: 4; size: 2; index to first column or column offset + relative flags
6946
6947
        // bit: 7-0; mask 0x00FF; column index
6948
        $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 4));
6949
6950
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6951
        if (!(0x4000 & self::getInt2d($subData, 4))) {
6952
            $fc = '$' . $fc;
6953
        }
6954
6955
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6956
        if (!(0x8000 & self::getInt2d($subData, 4))) {
6957
            $fr = '$' . $fr;
6958
        }
6959
6960
        // offset: 6; size: 2; index to last column or column offset + relative flags
6961
6962
        // bit: 7-0; mask 0x00FF; column index
6963
        $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex(0x00FF & self::getInt2d($subData, 6));
6964
6965
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6966
        if (!(0x4000 & self::getInt2d($subData, 6))) {
6967
            $lc = '$' . $lc;
6968
        }
6969
6970
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6971
        if (!(0x8000 & self::getInt2d($subData, 6))) {
6972
            $lr = '$' . $lr;
6973
        }
6974
6975
        return "$fc$fr:$lc$lr";
6976
    }
6977
6978
6979
    /**
6980
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
6981
     * to indicate offsets from a base cell
6982
     * section 3.3.4
6983
     *
6984
     * @param string $subData
6985
     * @param string $baseCell Base cell
6986
     * @return string Cell range address
6987
     */
6988
    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
6989
    {
6990
        list($baseCol, $baseRow) = \PhpSpreadsheet\Cell::coordinateFromString($baseCell);
6991
        $baseCol = \PhpSpreadsheet\Cell::columnIndexFromString($baseCol) - 1;
6992
6993
        // TODO: if cell range is just a single cell, should this funciton
6994
        // not just return e.g. 'A1' and not 'A1:A1' ?
6995
6996
        // offset: 0; size: 2; first row
6997
        $frIndex = self::getInt2d($subData, 0); // adjust below
6998
6999
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
7000
        $lrIndex = self::getInt2d($subData, 2); // adjust below
7001
7002
        // offset: 4; size: 2; first column with relative/absolute flags
7003
7004
        // bit: 7-0; mask 0x00FF; column index
7005
        $fcIndex = 0x00FF & self::getInt2d($subData, 4);
7006
7007
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
7008 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...
7009
            // absolute column index
7010
            $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($fcIndex);
7011
            $fc = '$' . $fc;
7012
        } else {
7013
            // column offset
7014
            $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
7015
            $fc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $fcIndex);
7016
        }
7017
7018
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
7019 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...
7020
            // absolute row index
7021
            $fr = $frIndex + 1;
7022
            $fr = '$' . $fr;
7023
        } else {
7024
            // row offset
7025
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
7026
            $fr = $baseRow + $frIndex;
7027
        }
7028
7029
        // offset: 6; size: 2; last column with relative/absolute flags
7030
7031
        // bit: 7-0; mask 0x00FF; column index
7032
        $lcIndex = 0x00FF & self::getInt2d($subData, 6);
7033
        $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
7034
        $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...
7035
7036
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
7037 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...
7038
            // absolute column index
7039
            $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($lcIndex);
7040
            $lc = '$' . $lc;
7041
        } else {
7042
            // column offset
7043
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
7044
            $lc = \PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
7045
        }
7046
7047
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
7048 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...
7049
            // absolute row index
7050
            $lr = $lrIndex + 1;
7051
            $lr = '$' . $lr;
7052
        } else {
7053
            // row offset
7054
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
7055
            $lr = $baseRow + $lrIndex;
7056
        }
7057
7058
        return "$fc$fr:$lc$lr";
7059
    }
7060
7061
7062
    /**
7063
     * Read BIFF8 cell range address list
7064
     * section 2.5.15
7065
     *
7066
     * @param string $subData
7067
     * @return array
7068
     */
7069 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...
7070
    {
7071
        $cellRangeAddresses = array();
7072
7073
        // offset: 0; size: 2; number of the following cell range addresses
7074
        $nm = self::getInt2d($subData, 0);
7075
7076
        $offset = 2;
7077
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
7078
        for ($i = 0; $i < $nm; ++$i) {
7079
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
7080
            $offset += 8;
7081
        }
7082
7083
        return array(
7084
            'size' => 2 + 8 * $nm,
7085
            'cellRangeAddresses' => $cellRangeAddresses,
7086
        );
7087
    }
7088
7089
7090
    /**
7091
     * Read BIFF5 cell range address list
7092
     * section 2.5.15
7093
     *
7094
     * @param string $subData
7095
     * @return array
7096
     */
7097 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...
7098
    {
7099
        $cellRangeAddresses = array();
7100
7101
        // offset: 0; size: 2; number of the following cell range addresses
7102
        $nm = self::getInt2d($subData, 0);
7103
7104
        $offset = 2;
7105
        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
7106
        for ($i = 0; $i < $nm; ++$i) {
7107
            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
7108
            $offset += 6;
7109
        }
7110
7111
        return array(
7112
            'size' => 2 + 6 * $nm,
7113
            'cellRangeAddresses' => $cellRangeAddresses,
7114
        );
7115
    }
7116
7117
7118
    /**
7119
     * Get a sheet range like Sheet1:Sheet3 from REF index
7120
     * Note: If there is only one sheet in the range, one gets e.g Sheet1
7121
     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
7122
     * in which case an Exception is thrown
7123
     *
7124
     * @param int $index
7125
     * @return string|false
7126
     * @throws Exception
7127
     */
7128
    private function readSheetRangeByRefIndex($index)
7129
    {
7130
        if (isset($this->ref[$index])) {
7131
            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
7132
7133
            switch ($type) {
7134
                case 'internal':
7135
                    // check if we have a deleted 3d reference
7136
                    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...
7137
                        throw new Exception('Deleted sheet reference');
7138
                    }
7139
7140
                    // we have normal sheet range (collapsed or uncollapsed)
7141
                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
7142
                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
7143
7144
                    if ($firstSheetName == $lastSheetName) {
7145
                        // collapsed sheet range
7146
                        $sheetRange = $firstSheetName;
7147
                    } else {
7148
                        $sheetRange = "$firstSheetName:$lastSheetName";
7149
                    }
7150
7151
                    // escape the single-quotes
7152
                    $sheetRange = str_replace("'", "''", $sheetRange);
7153
7154
                    // if there are special characters, we need to enclose the range in single-quotes
7155
                    // todo: check if we have identified the whole set of special characters
7156
                    // it seems that the following characters are not accepted for sheet names
7157
                    // and we may assume that they are not present: []*/:\?
7158
                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
7159
                        $sheetRange = "'$sheetRange'";
7160
                    }
7161
7162
                    return $sheetRange;
7163
                    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...
7164
                default:
7165
                    // TODO: external sheet support
7166
                    throw new Exception('Excel5 reader only supports internal sheets in fomulas');
7167
                    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...
7168
            }
7169
        }
7170
        return false;
7171
    }
7172
7173
7174
    /**
7175
     * read BIFF8 constant value array from array data
7176
     * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
7177
     * section 2.5.8
7178
     *
7179
     * @param string $arrayData
7180
     * @return array
7181
     */
7182
    private static function readBIFF8ConstantArray($arrayData)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
7183
    {
7184
        // offset: 0; size: 1; number of columns decreased by 1
7185
        $nc = ord($arrayData[0]);
7186
7187
        // offset: 1; size: 2; number of rows decreased by 1
7188
        $nr = self::getInt2d($arrayData, 1);
7189
        $size = 3; // initialize
7190
        $arrayData = substr($arrayData, 3);
7191
7192
        // 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...
7193
        $matrixChunks = array();
7194
        for ($r = 1; $r <= $nr + 1; ++$r) {
7195
            $items = array();
7196
            for ($c = 1; $c <= $nc + 1; ++$c) {
7197
                $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...
7198
                $items[] = $constant['value'];
7199
                $arrayData = substr($arrayData, $constant['size']);
7200
                $size += $constant['size'];
7201
            }
7202
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
7203
        }
7204
        $matrix = '{' . implode(';', $matrixChunks) . '}';
7205
7206
        return array(
7207
            'value' => $matrix,
7208
            'size' => $size,
7209
        );
7210
    }
7211
7212
7213
    /**
7214
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
7215
     * section 2.5.7
7216
     * returns e.g. array('value' => '5', 'size' => 9)
7217
     *
7218
     * @param string $valueData
7219
     * @return array
7220
     */
7221
    private static function readBIFF8Constant($valueData)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
7222
    {
7223
        // offset: 0; size: 1; identifier for type of constant
7224
        $identifier = ord($valueData[0]);
7225
7226
        switch ($identifier) {
7227
            case 0x00: // empty constant (what is this?)
7228
                $value = '';
7229
                $size = 9;
7230
                break;
7231
            case 0x01: // number
7232
                // offset: 1; size: 8; IEEE 754 floating-point value
7233
                $value = self::extractNumber(substr($valueData, 1, 8));
7234
                $size = 9;
7235
                break;
7236
            case 0x02: // string value
7237
                // offset: 1; size: var; Unicode string, 16-bit string length
7238
                $string = self::readUnicodeStringLong(substr($valueData, 1));
7239
                $value = '"' . $string['value'] . '"';
7240
                $size = 1 + $string['size'];
7241
                break;
7242
            case 0x04: // boolean
7243
                // 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...
7244
                if (ord($valueData[1])) {
7245
                    $value = 'TRUE';
7246
                } else {
7247
                    $value = 'FALSE';
7248
                }
7249
                $size = 9;
7250
                break;
7251
            case 0x10: // error code
7252
                // offset: 1; size: 1; error code
7253
                $value = Excel5\ErrorCode::lookup(ord($valueData[1]));
7254
                $size = 9;
7255
                break;
7256
        }
7257
        return array(
7258
            '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...
7259
            '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...
7260
        );
7261
    }
7262
7263
7264
    /**
7265
     * Extract RGB color
7266
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
7267
     *
7268
     * @param string $rgb Encoded RGB value (4 bytes)
7269
     * @return array
7270
     */
7271
    private static function readRGB($rgb)
7272
    {
7273
        // offset: 0; size 1; Red component
7274
        $r = ord($rgb{0});
7275
7276
        // offset: 1; size: 1; Green component
7277
        $g = ord($rgb{1});
7278
7279
        // offset: 2; size: 1; Blue component
7280
        $b = ord($rgb{2});
7281
7282
        // HEX notation, e.g. 'FF00FC'
7283
        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
7284
7285
        return array('rgb' => $rgb);
7286
    }
7287
7288
7289
    /**
7290
     * Read byte string (8-bit string length)
7291
     * OpenOffice documentation: 2.5.2
7292
     *
7293
     * @param string $subData
7294
     * @return array
7295
     */
7296 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...
7297
    {
7298
        // offset: 0; size: 1; length of the string (character count)
7299
        $ln = ord($subData[0]);
7300
7301
        // 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...
7302
        $value = $this->decodeCodepage(substr($subData, 1, $ln));
7303
7304
        return array(
7305
            'value' => $value,
7306
            'size' => 1 + $ln, // size in bytes of data structure
7307
        );
7308
    }
7309
7310
7311
    /**
7312
     * Read byte string (16-bit string length)
7313
     * OpenOffice documentation: 2.5.2
7314
     *
7315
     * @param string $subData
7316
     * @return array
7317
     */
7318 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...
7319
    {
7320
        // offset: 0; size: 2; length of the string (character count)
7321
        $ln = self::getInt2d($subData, 0);
7322
7323
        // 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...
7324
        $value = $this->decodeCodepage(substr($subData, 2));
7325
7326
        //return $string;
7327
        return array(
7328
            'value' => $value,
7329
            'size' => 2 + $ln, // size in bytes of data structure
7330
        );
7331
    }
7332
7333
7334
    /**
7335
     * Extracts an Excel Unicode short string (8-bit string length)
7336
     * OpenOffice documentation: 2.5.3
7337
     * function will automatically find out where the Unicode string ends.
7338
     *
7339
     * @param string $subData
7340
     * @return array
7341
     */
7342 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...
7343
    {
7344
        $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...
7345
7346
        // offset: 0: size: 1; length of the string (character count)
7347
        $characterCount = ord($subData[0]);
7348
7349
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7350
7351
        // add 1 for the string length
7352
        $string['size'] += 1;
7353
7354
        return $string;
7355
    }
7356
7357
7358
    /**
7359
     * Extracts an Excel Unicode long string (16-bit string length)
7360
     * OpenOffice documentation: 2.5.3
7361
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7362
     *
7363
     * @param string $subData
7364
     * @return array
7365
     */
7366 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...
7367
    {
7368
        $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...
7369
7370
        // offset: 0: size: 2; length of the string (character count)
7371
        $characterCount = self::getInt2d($subData, 0);
7372
7373
        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7374
7375
        // add 2 for the string length
7376
        $string['size'] += 2;
7377
7378
        return $string;
7379
    }
7380
7381
7382
    /**
7383
     * Read Unicode string with no string length field, but with known character count
7384
     * this function is under construction, needs to support rich text, and Asian phonetic settings
7385
     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
7386
     *
7387
     * @param string $subData
7388
     * @param int $characterCount
7389
     * @return array
7390
     */
7391
    private static function readUnicodeString($subData, $characterCount)
7392
    {
7393
        $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...
7394
7395
        // offset: 0: size: 1; option flags
7396
        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
7397
        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7398
7399
        // bit: 2; mask: 0x04; Asian phonetic settings
7400
        $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...
7401
7402
        // bit: 3; mask: 0x08; Rich-Text settings
7403
        $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...
7404
7405
        // 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...
7406
        // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
7407
        // needs to be fixed
7408
        $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
7409
7410
        return array(
7411
            'value' => $value,
7412
            'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
7413
        );
7414
    }
7415
7416
7417
    /**
7418
     * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
7419
     * Example:  hello"world  -->  "hello""world"
7420
     *
7421
     * @param string $value UTF-8 encoded string
7422
     * @return string
7423
     */
7424
    private static function UTF8toExcelDoubleQuoted($value)
7425
    {
7426
        return '"' . str_replace('"', '""', $value) . '"';
7427
    }
7428
7429
7430
    /**
7431
     * Reads first 8 bytes of a string and return IEEE 754 float
7432
     *
7433
     * @param string $data Binary string that is at least 8 bytes long
7434
     * @return float
7435
     */
7436
    private static function extractNumber($data)
7437
    {
7438
        $rknumhigh = self::getInt4d($data, 4);
7439
        $rknumlow = self::getInt4d($data, 0);
7440
        $sign = ($rknumhigh & 0x80000000) >> 31;
7441
        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
7442
        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
7443
        $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
7444
        $mantissalow2 = ($rknumlow & 0x7fffffff);
7445
        $value = $mantissa / pow(2, (20 - $exp));
7446
7447
        if ($mantissalow1 != 0) {
7448
            $value += 1 / pow(2, (21 - $exp));
7449
        }
7450
7451
        $value += $mantissalow2 / pow(2, (52 - $exp));
7452
        if ($sign) {
7453
            $value *= -1;
7454
        }
7455
7456
        return $value;
7457
    }
7458
7459
7460
    private static function getIEEE754($rknum)
7461
    {
7462
        if (($rknum & 0x02) != 0) {
7463
            $value = $rknum >> 2;
7464
        } else {
7465
            // changes by mmp, info on IEEE754 encoding from
7466
            // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
7467
            // The RK format calls for using only the most significant 30 bits
7468
            // of the 64 bit floating point value. The other 34 bits are assumed
7469
            // to be 0 so we use the upper 30 bits of $rknum as follows...
7470
            $sign = ($rknum & 0x80000000) >> 31;
7471
            $exp = ($rknum & 0x7ff00000) >> 20;
7472
            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
7473
            $value = $mantissa / pow(2, (20- ($exp - 1023)));
7474
            if ($sign) {
7475
                $value = -1 * $value;
7476
            }
7477
            //end of changes by mmp
7478
        }
7479
        if (($rknum & 0x01) != 0) {
7480
            $value /= 100;
7481
        }
7482
        return $value;
7483
    }
7484
7485
7486
    /**
7487
     * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
7488
     *
7489
     * @param string $string
7490
     * @param bool $compressed
7491
     * @return string
7492
     */
7493
    private static function encodeUTF16($string, $compressed = '')
7494
    {
7495
        if ($compressed) {
7496
            $string = self::uncompressByteString($string);
7497
        }
7498
7499
        return \PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
7500
    }
7501
7502
7503
    /**
7504
     * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
7505
     *
7506
     * @param string $string
7507
     * @return string
7508
     */
7509
    private static function uncompressByteString($string)
7510
    {
7511
        $uncompressedString = '';
7512
        $strLen = strlen($string);
7513
        for ($i = 0; $i < $strLen; ++$i) {
7514
            $uncompressedString .= $string[$i] . "\0";
7515
        }
7516
7517
        return $uncompressedString;
7518
    }
7519
7520
7521
    /**
7522
     * Convert string to UTF-8. Only used for BIFF5.
7523
     *
7524
     * @param string $string
7525
     * @return string
7526
     */
7527
    private function decodeCodepage($string)
7528
    {
7529
        return \PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
7530
    }
7531
7532
7533
    /**
7534
     * Read 16-bit unsigned integer
7535
     *
7536
     * @param string $data
7537
     * @param int $pos
7538
     * @return int
7539
     */
7540
    public static function getInt2d($data, $pos)
7541
    {
7542
        return ord($data[$pos]) | (ord($data[$pos+1]) << 8);
7543
    }
7544
7545
7546
    /**
7547
     * Read 32-bit signed integer
7548
     *
7549
     * @param string $data
7550
     * @param int $pos
7551
     * @return int
7552
     */
7553 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...
7554
    {
7555
        // FIX: represent numbers correctly on 64-bit system
7556
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
7557
        // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
7558
        $_or_24 = ord($data[$pos + 3]);
7559
        if ($_or_24 >= 128) {
7560
            // negative number
7561
            $_ord_24 = -abs((256 - $_or_24) << 24);
7562
        } else {
7563
            $_ord_24 = ($_or_24 & 127) << 24;
7564
        }
7565
        return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $_ord_24;
7566
    }
7567
7568
7569
    private function parseRichText($is = '')
7570
    {
7571
        $value = new \PhpSpreadsheet\RichText();
7572
        $value->createText($is);
7573
7574
        return $value;
7575
    }
7576
}
7577