Completed
Push — develop ( 7b90bb...a1a034 )
by Adrien
30:11
created

Xls   F

Complexity

Total Complexity 1129

Size/Duplication

Total Lines 7416
Duplicated Lines 11.29 %

Coupling/Cohesion

Components 1
Dependencies 39

Test Coverage

Coverage 46.69%

Importance

Changes 0
Metric Value
dl 837
loc 7416
ccs 1845
cts 3952
cp 0.4669
rs 0.5217
c 0
b 0
f 0
wmc 1129
lcom 1
cbo 39

111 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A canRead() 0 16 2
C listWorksheetNames() 18 46 7
D listWorksheetInfo() 18 96 16
F load() 34 594 134
C readRecordData() 0 46 9
A loadOLE() 0 13 1
D readSummaryInformation() 10 130 28
D readDocumentSummaryInformation() 10 125 27
A readDefault() 0 7 1
B readNote() 0 49 5
B readTextObject() 0 43 3
C readBof() 0 33 7
A readFilepass() 0 22 3
A makeKey() 0 23 2
C verifyPassword() 0 72 8
A readCodepage() 13 13 1
A readDateMode() 0 14 2
F readFont() 5 86 13
A readFormat() 6 22 3
F readXf() 15 277 48
D readXfExt() 120 164 27
B readStyle() 0 33 4
A readPalette() 0 19 3
B readSheet() 7 47 6
B readExternalBook() 0 58 6
B readExternName() 0 30 2
B readExternSheet() 0 24 3
A readDefinedName() 0 50 3
A readMsoDrawingGroup() 10 10 1
F readSst() 0 174 23
A readPrintGridlines() 0 14 3
A readDefaultRowHeight() 13 13 1
A readSheetPr() 0 22 1
B readHorizontalPageBreaks() 23 23 4
B readVerticalPageBreaks() 23 23 4
B readHeader() 23 23 4
B readFooter() 22 22 4
A readHcenter() 15 15 2
A readVcenter() 15 15 2
A readLeftMargin() 0 13 2
A readRightMargin() 0 13 2
A readTopMargin() 0 13 2
A readBottomMargin() 0 13 2
B readPageSetup() 0 56 5
A readProtect() 18 18 2
A readScenProtect() 19 19 2
A readObjectProtect() 19 19 2
A readPassword() 0 14 2
A readDefColWidth() 14 14 2
B readColInfo() 0 46 5
B readRow() 0 57 5
B readRk() 34 34 4
C readLabelSst() 0 75 17
B readMulRk() 0 44 5
B readNumber() 32 32 4
F readFormula() 14 120 24
A readSharedFmla() 0 23 1
A readString() 0 18 2
B readBoolErr() 0 48 6
B readMulBlank() 0 30 6
C readLabel() 7 40 7
B readBlank() 0 26 5
A readMsoDrawing() 10 10 1
B readObj() 0 36 3
C readWindow2() 0 67 9
B readPageLayoutView() 0 33 2
A readScl() 0 17 1
A readPane() 0 22 3
B readSelection() 0 46 5
B includeCellRangeFiltered() 0 19 5
B readMergedCells() 0 18 6
F readHyperLink() 0 167 20
A readDataValidations() 0 8 1
F readDataValidation() 63 187 30
B readSheetLayout() 0 34 4
B readSheetProtection() 0 94 3
B readRangeProtection() 0 56 6
B readImData() 0 69 6
B readContinue() 0 42 4
B getSplicedRecordData() 0 30 2
A getFormulaFromStructure() 0 17 2
A getFormulaFromData() 0 14 3
F createFormulaFromTokens() 16 150 63
F getNextToken() 55 1278 333
A readBIFF8CellAddress() 0 20 3
B readBIFF8CellAddressB() 7 32 5
B readBIFF5CellRangeAddressFixed() 0 29 5
B readBIFF8CellRangeAddressFixed() 0 29 5
B readBIFF8CellRangeAddress() 0 43 5
C readBIFF8CellRangeAddressB() 36 72 10
A readBIFF8CellRangeAddressList() 19 19 2
A readBIFF5CellRangeAddressList() 19 19 2
C readSheetRangeByRefIndex() 0 45 7
B readBIFF8ConstantArray() 0 29 3
C readBIFF8Constant() 0 42 7
A readRGB() 0 16 1
A readByteStringShort() 13 13 1
A readByteStringLong() 14 14 1
A readUnicodeStringShort() 14 14 1
A readUnicodeStringLong() 14 14 1
B readUnicodeString() 0 24 3
A UTF8toExcelDoubleQuoted() 0 4 1
A extractNumber() 0 22 3
B getIEEE754() 0 25 4
A encodeUTF16() 0 8 2
A uncompressByteString() 0 10 2
A decodeCodepage() 0 4 1
A getInt2d() 0 4 1
A getInt4d() 0 15 2
A parseRichText() 0 7 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Xls often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Xls, and based on these observations, apply Extract Interface, too.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$answer = 42;

$correct = false;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

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

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

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

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

Loading history...
966 4
                    case self::XLS_TYPE_TXO:
967 1
                        $this->readTextObject();
968 1
                        break;
969 4
                    case self::XLS_TYPE_CONTINUE:
970
                        $this->readContinue();
971
                        break;
972 4
                    case self::XLS_TYPE_EOF:
973 4
                        $this->readDefault();
974 4
                        break 2;
975
                    default:
976 4
                        $this->readDefault();
977 4
                        break;
978
                }
979
            }
980
981
            // treat MSODRAWING records, sheet-level Escher
982 4
            if (!$this->readDataOnly && $this->drawingData) {
983 3
                $escherWorksheet = new \PhpOffice\PhpSpreadsheet\Shared\Escher();
984 3
                $reader = new Xls\Escher($escherWorksheet);
985 3
                $escherWorksheet = $reader->load($this->drawingData);
986
987
                // get all spContainers in one long array, so they can be mapped to OBJ records
988 3
                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
989
            }
990
991
            // treat OBJ records
992 4
            foreach ($this->objs as $n => $obj) {
993
                // the first shape container never has a corresponding OBJ record, hence $n + 1
994 3
                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
995 3
                    $spContainer = $allSpContainers[$n + 1];
996
997
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
998 3
                    if ($spContainer->getNestingLevel() > 1) {
999
                        continue;
1000
                    }
1001
1002
                    // calculate the width and height of the shape
1003 3
                    list($startColumn, $startRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($spContainer->getStartCoordinates());
1004 3
                    list($endColumn, $endRow) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($spContainer->getEndCoordinates());
1005
1006 3
                    $startOffsetX = $spContainer->getStartOffsetX();
1007 3
                    $startOffsetY = $spContainer->getStartOffsetY();
1008 3
                    $endOffsetX = $spContainer->getEndOffsetX();
1009 3
                    $endOffsetY = $spContainer->getEndOffsetY();
1010
1011 3
                    $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
1012 3
                    $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
1013
1014
                    // calculate offsetX and offsetY of the shape
1015 3
                    $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024;
1016 3
                    $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256;
1017
1018 3
                    switch ($obj['otObjType']) {
1019 3
                        case 0x19:
1020
                            // Note
1021 1
                            if (isset($this->cellNotes[$obj['idObjID']])) {
1022 1
                                $cellNote = $this->cellNotes[$obj['idObjID']];
0 ignored issues
show
Unused Code introduced by
$cellNote is not used, you could remove the assignment.

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

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

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

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

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

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

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

Loading history...
1025 1
                                    $textObject = $this->textObjects[$obj['idObjID']];
1026 1
                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
1027
                                }
1028
                            }
1029 1
                            break;
1030 3
                        case 0x08:
1031
                            // picture
1032
                            // get index to BSE entry (1-based)
1033 3
                            $BSEindex = $spContainer->getOPT(0x0104);
1034
1035
                            // If there is no BSE Index, we will fail here and other fields are not read.
1036
                            // Fix by checking here.
1037
                            // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
1038
                            // More likely : a uncompatible picture
1039 3
                            if (!$BSEindex) {
1040
                                continue;
1041
                            }
1042
1043 3
                            $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 3
                            $BSE = $BSECollection[$BSEindex - 1];
1045 3
                            $blipType = $BSE->getBlipType();
1046
1047
                            // need check because some blip types are not supported by Escher reader such as EMF
1048 3
                            if ($blip = $BSE->getBlip()) {
1049 3
                                $ih = imagecreatefromstring($blip->getData());
1050 3
                                $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing();
1051 3
                                $drawing->setImageResource($ih);
1052
1053
                                // width, height, offsetX, offsetY
1054 3
                                $drawing->setResizeProportional(false);
1055 3
                                $drawing->setWidth($width);
1056 3
                                $drawing->setHeight($height);
1057 3
                                $drawing->setOffsetX($offsetX);
1058 3
                                $drawing->setOffsetY($offsetY);
1059
1060
                                switch ($blipType) {
1061 3
                                    case \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_JPEG:
1062 3
                                        $drawing->setRenderingFunction(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_JPEG);
1063 3
                                        $drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_JPEG);
1064 3
                                        break;
1065 3
                                    case \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::BLIPTYPE_PNG:
1066 3
                                        $drawing->setRenderingFunction(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::RENDERING_PNG);
1067 3
                                        $drawing->setMimeType(\PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::MIMETYPE_PNG);
1068 3
                                        break;
1069
                                }
1070
1071 3
                                $drawing->setWorksheet($this->phpSheet);
1072 3
                                $drawing->setCoordinates($spContainer->getStartCoordinates());
1073
                            }
1074 3
                            break;
1075
                        default:
1076
                            // other object type
1077 3
                            break;
1078
                    }
1079
                }
1080
            }
1081
1082
            // treat SHAREDFMLA records
1083 4
            if ($this->version == self::XLS_BIFF8) {
1084 4
                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
1085
                    list($column, $row) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($cell);
1086
                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
1087
                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
1088
                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA);
1089
                    }
1090
                }
1091
            }
1092
1093 4
            if (!empty($this->cellNotes)) {
1094 1
                foreach ($this->cellNotes as $note => $noteDetails) {
1095 1
                    if (!isset($noteDetails['objTextData'])) {
1096 View Code Duplication
                        if (isset($this->textObjects[$note])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
1167
                                            // then we have repeating columns
1168
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
1169
                                        }
1170
                                    }
1171
                                }
1172
                            }
1173
                        }
1174
                        break;
1175
                }
1176
            } else {
1177
                // Extract range
1178 1
                $explodes = explode('!', $definedName['formula']);
1179
1180 1
                if (count($explodes) == 2) {
1181 1
                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1182 1
                        ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $docSheet is correct as $this->spreadsheet->getS...im($explodes[0], '\'')) (which targets PhpOffice\PhpSpreadsheet...sheet::getSheetByName()) seems to always return null.

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

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

}

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

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

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

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

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

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

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

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

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

Loading history...
1288
        // offset: 4; size: 2; OS version
1289
        // offset: 6; size: 2; OS indicator
1290
        // offset: 8; size: 16
1291
        // offset: 24; size: 4; section count
1292 4
        $secCount = self::getInt4d($this->summaryInformation, 24);
0 ignored issues
show
Unused Code introduced by
$secCount is not used, you could remove the assignment.

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

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

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

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

Loading history...
1293
1294
        // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
1295
        // offset: 44; size: 4
1296 4
        $secOffset = self::getInt4d($this->summaryInformation, 44);
1297
1298
        // section header
1299
        // offset: $secOffset; size: 4; section length
1300 4
        $secLength = self::getInt4d($this->summaryInformation, $secOffset);
0 ignored issues
show
Unused Code introduced by
$secLength is not used, you could remove the assignment.

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

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

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

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

Loading history...
1301
1302
        // offset: $secOffset+4; size: 4; property count
1303 4
        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1304
1305
        // initialize code page (used to resolve string values)
1306 4
        $codePage = 'CP1252';
1307
1308
        // offset: ($secOffset+8); size: var
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1316 4
            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
1317
1318 4
            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1319
1320
            // initialize property value
1321 4
            $value = null;
1322
1323
            // extract property value based on property type
1324
            switch ($type) {
1325 4
                case 0x02: // 2 byte signed integer
1326 4
                    $value = self::getInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1327 4
                    break;
1328 4
                case 0x03: // 4 byte signed integer
1329 4
                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1330 4
                    break;
1331 4
                case 0x13: // 4 byte unsigned integer
1332
                    // not needed yet, fix later if necessary
1333
                    break;
1334 4 View Code Duplication
                case 0x1E: // null-terminated string prepended by dword string length
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

Loading history...
1422
        //    offset: 4;    size: 2;    OS version
1423
        //    offset: 6;    size: 2;    OS indicator
1424
        //    offset: 8;    size: 16
1425
        //    offset: 24;    size: 4;    section count
1426 4
        $secCount = self::getInt4d($this->documentSummaryInformation, 24);
0 ignored issues
show
Unused Code introduced by
$secCount is not used, you could remove the assignment.

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

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

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

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

Loading history...
1427
1428
        // offset: 28;    size: 16;    first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
1429
        // offset: 44;    size: 4;    first section offset
1430 4
        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1431
1432
        //    section header
1433
        //    offset: $secOffset;    size: 4;    section length
1434 4
        $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
0 ignored issues
show
Unused Code introduced by
$secLength is not used, you could remove the assignment.

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

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

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

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

Loading history...
1435
1436
        //    offset: $secOffset+4;    size: 4;    property count
1437 4
        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1438
1439
        // initialize code page (used to resolve string values)
1440 4
        $codePage = 'CP1252';
1441
1442
        //    offset: ($secOffset+8);    size: var
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

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

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

Loading history...
1446 4
            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
1447
1448
            // Use value of property id as appropriate
1449
            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
1450 4
            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
1451
1452 4
            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1453
1454
            // initialize property value
1455 4
            $value = null;
1456
1457
            // extract property value based on property type
1458
            switch ($type) {
1459 4
                case 0x02:    //    2 byte signed integer
1460 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1461 4
                    break;
1462 4
                case 0x03:    //    4 byte signed integer
1463 4
                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1464 4
                    break;
1465 4
                case 0x0B:  // Boolean
1466 4
                    $value = self::getInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1467 4
                    $value = ($value == 0 ? false : true);
1468 4
                    break;
1469 4
                case 0x13:    //    4 byte unsigned integer
1470
                    // not needed yet, fix later if necessary
1471
                    break;
1472 4 View Code Duplication
                case 0x1E:    //    null-terminated string prepended by dword string length
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
1479
                    // PHP-Time
1480
                    $value = \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1481
                    break;
1482 4
                case 0x47:    //    Clipboard format
1483
                    // not needed yet, fix later if necessary
1484
                    break;
1485
            }
1486
1487
            switch ($id) {
1488 4
                case 0x01:    //    Code Page
1489 4
                    $codePage = \PhpOffice\PhpSpreadsheet\Shared\CodePage::numberToName($value);
1490 4
                    break;
1491 4
                case 0x02:    //    Category
1492 3
                    $this->spreadsheet->getProperties()->setCategory($value);
1493 3
                    break;
1494 4
                case 0x03:    //    Presentation Target
1495
                    //    Not supported by PhpSpreadsheet
1496
                    break;
1497 4
                case 0x04:    //    Bytes
1498
                    //    Not supported by PhpSpreadsheet
1499
                    break;
1500 4
                case 0x05:    //    Lines
1501
                    //    Not supported by PhpSpreadsheet
1502
                    break;
1503 4
                case 0x06:    //    Paragraphs
1504
                    //    Not supported by PhpSpreadsheet
1505
                    break;
1506 4
                case 0x07:    //    Slides
1507
                    //    Not supported by PhpSpreadsheet
1508
                    break;
1509 4
                case 0x08:    //    Notes
1510
                    //    Not supported by PhpSpreadsheet
1511
                    break;
1512 4
                case 0x09:    //    Hidden Slides
1513
                    //    Not supported by PhpSpreadsheet
1514
                    break;
1515 4
                case 0x0A:    //    MM Clips
1516
                    //    Not supported by PhpSpreadsheet
1517
                    break;
1518 4
                case 0x0B:    //    Scale Crop
1519
                    //    Not supported by PhpSpreadsheet
1520 4
                    break;
1521 4
                case 0x0C:    //    Heading Pairs
1522
                    //    Not supported by PhpSpreadsheet
1523 4
                    break;
1524 4
                case 0x0D:    //    Titles of Parts
1525
                    //    Not supported by PhpSpreadsheet
1526 4
                    break;
1527 4
                case 0x0E:    //    Manager
1528 1
                    $this->spreadsheet->getProperties()->setManager($value);
1529 1
                    break;
1530 4
                case 0x0F:    //    Company
1531 3
                    $this->spreadsheet->getProperties()->setCompany($value);
1532 3
                    break;
1533 4
                case 0x10:    //    Links up-to-date
1534
                    //    Not supported by PhpSpreadsheet
1535 4
                    break;
1536
            }
1537
        }
1538 4
    }
1539
1540
    /**
1541
     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1542
     */
1543 4
    private function readDefault()
1544
    {
1545 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1546
1547
        // move stream pointer to next record
1548 4
        $this->pos += 4 + $length;
1549 4
    }
1550
1551
    /**
1552
     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1553
     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1554
     */
1555 1
    private function readNote()
1556
    {
1557 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1558 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1559
1560
        // move stream pointer to next record
1561 1
        $this->pos += 4 + $length;
1562
1563 1
        if ($this->readDataOnly) {
1564
            return;
1565
        }
1566
1567 1
        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
1568 1
        if ($this->version == self::XLS_BIFF8) {
1569 1
            $noteObjID = self::getInt2d($recordData, 6);
1570 1
            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1571 1
            $noteAuthor = $noteAuthor['value'];
1572 1
            $this->cellNotes[$noteObjID] = [
1573 1
                'cellRef' => $cellAddress,
1574 1
                'objectID' => $noteObjID,
1575 1
                'author' => $noteAuthor,
1576
            ];
1577
        } else {
1578
            $extension = false;
1579
            if ($cellAddress == '$B$65536') {
1580
                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1581
                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1582
                //        max 2048 bytes will probably throw a wobbly.
1583
                $row = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$row is not used, you could remove the assignment.

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1601
            }
1602
        }
1603 1
    }
1604
1605
    /**
1606
     * The TEXT Object record contains the text associated with a cell annotation.
1607
     */
1608 1
    private function readTextObject()
1609
    {
1610 1
        $length = self::getInt2d($this->data, $this->pos + 2);
1611 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1612
1613
        // move stream pointer to next record
1614 1
        $this->pos += 4 + $length;
1615
1616 1
        if ($this->readDataOnly) {
1617
            return;
1618
        }
1619
1620
        // recordData consists of an array of subrecords looking like this:
1621
        //    grbit: 2 bytes; Option Flags
1622
        //    rot: 2 bytes; rotation
1623
        //    cchText: 2 bytes; length of the text (in the first continue record)
1624
        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
1625
        // followed by the continuation records containing the actual text and formatting
1626 1
        $grbitOpts = self::getInt2d($recordData, 0);
1627 1
        $rot = self::getInt2d($recordData, 2);
1628 1
        $cchText = self::getInt2d($recordData, 10);
0 ignored issues
show
Unused Code introduced by
$cchText 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...
1629 1
        $cbRuns = self::getInt2d($recordData, 12);
1630 1
        $text = $this->getSplicedRecordData();
1631
1632 1
        $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1;
1633 1
        $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte);
1634
        // get 1 byte
1635 1
        $is16Bit = ord($text['recordData'][0]);
1636
        // it is possible to use a compressed format,
1637
        // which omits the high bytes of all characters, if they are all zero
1638 1
        if (($is16Bit & 0x01) === 0) {
1639 1
            $textStr = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1');
1640
        } else {
1641
            $textStr = $this->decodeCodepage($textStr);
1642
        }
1643
1644 1
        $this->textObjects[$this->textObjRef] = [
1645 1
            'text' => $textStr,
1646 1
            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
1647 1
            'alignment' => $grbitOpts,
1648 1
            'rotation' => $rot,
1649
        ];
1650 1
    }
1651
1652
    /**
1653
     * Read BOF.
1654
     */
1655 4
    private function readBof()
1656
    {
1657 4
        $length = self::getInt2d($this->data, $this->pos + 2);
1658 4
        $recordData = substr($this->data, $this->pos + 4, $length);
1659
1660
        // move stream pointer to next record
1661 4
        $this->pos += 4 + $length;
1662
1663
        // offset: 2; size: 2; type of the following data
1664 4
        $substreamType = self::getInt2d($recordData, 2);
1665
1666
        switch ($substreamType) {
1667 4
            case self::XLS_WORKBOOKGLOBALS:
1668 4
                $version = self::getInt2d($recordData, 0);
1669 4
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1670
                    throw new Exception('Cannot read this Excel file. Version is too old.');
1671
                }
1672 4
                $this->version = $version;
1673 4
                break;
1674 4
            case self::XLS_WORKSHEET:
1675
                // do not use this version information for anything
1676
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1677 4
                break;
1678
            default:
1679
                // substream, e.g. chart
1680
                // just skip the entire substream
1681
                do {
1682
                    $code = self::getInt2d($this->data, $this->pos);
1683
                    $this->readDefault();
1684
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1685
                break;
1686
        }
1687 4
    }
1688
1689
    /**
1690
     * FILEPASS.
1691
     *
1692
     * This record is part of the File Protection Block. It
1693
     * contains information about the read/write password of the
1694
     * file. All record contents following this record will be
1695
     * encrypted.
1696
     *
1697
     * --    "OpenOffice.org's Documentation of the Microsoft
1698
     *         Excel File Format"
1699
     *
1700
     * The decryption functions and objects used from here on in
1701
     * are based on the source of Spreadsheet-ParseExcel:
1702
     * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/
1703
     */
1704
    private function readFilepass()
1705
    {
1706
        $length = self::getInt2d($this->data, $this->pos + 2);
1707
1708
        if ($length != 54) {
1709
            throw new Exception('Unexpected file pass record length');
1710
        }
1711
1712
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
1713
1714
        // move stream pointer to next record
1715
        $this->pos += 4 + $length;
1716
1717
        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1718
            throw new Exception('Decryption password incorrect');
1719
        }
1720
1721
        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1722
1723
        // Decryption required from the record after next onwards
1724
        $this->encryptionStartPos = $this->pos + self::getInt2d($this->data, $this->pos + 2);
1725
    }
1726
1727
    /**
1728
     * Make an RC4 decryptor for the given block.
1729
     *
1730
     * @param int Block for which to create decrypto
1731
     * @param string $valContext MD5 context state
1732
     * @param mixed $block
1733
     *
1734
     * @return Xls\RC4
1735
     */
1736
    private function makeKey($block, $valContext)
1737
    {
1738
        $pwarray = str_repeat("\0", 64);
1739
1740
        for ($i = 0; $i < 5; ++$i) {
1741
            $pwarray[$i] = $valContext[$i];
1742
        }
1743
1744
        $pwarray[5] = chr($block & 0xff);
1745
        $pwarray[6] = chr(($block >> 8) & 0xff);
1746
        $pwarray[7] = chr(($block >> 16) & 0xff);
1747
        $pwarray[8] = chr(($block >> 24) & 0xff);
1748
1749
        $pwarray[9] = "\x80";
1750
        $pwarray[56] = "\x48";
1751
1752
        $md5 = new Xls\MD5();
1753
        $md5->add($pwarray);
1754
1755
        $s = $md5->getContext();
1756
1757
        return new Xls\RC4($s);
1758
    }
1759
1760
    /**
1761
     * Verify RC4 file password.
1762
     *
1763
     * @param string $password Password to check
1764
     * @param string $docid Document id
1765
     * @param string $salt_data Salt data
1766
     * @param string $hashedsalt_data Hashed salt data
1767
     * @param string $valContext Set to the MD5 context of the value
1768
     *
1769
     * @return bool Success
1770
     */
1771
    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
1772
    {
1773
        $pwarray = str_repeat("\0", 64);
1774
1775
        for ($i = 0; $i < strlen($password); ++$i) {
1776
            $o = ord(substr($password, $i, 1));
1777
            $pwarray[2 * $i] = chr($o & 0xff);
1778
            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
1779
        }
1780
        $pwarray[2 * $i] = chr(0x80);
1781
        $pwarray[56] = chr(($i << 4) & 0xff);
1782
1783
        $md5 = new Xls\MD5();
1784
        $md5->add($pwarray);
1785
1786
        $mdContext1 = $md5->getContext();
1787
1788
        $offset = 0;
1789
        $keyoffset = 0;
1790
        $tocopy = 5;
1791
1792
        $md5->reset();
1793
1794
        while ($offset != 16) {
1795
            if ((64 - $offset) < 5) {
1796
                $tocopy = 64 - $offset;
1797
            }
1798
            for ($i = 0; $i <= $tocopy; ++$i) {
1799
                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
1800
            }
1801
            $offset += $tocopy;
1802
1803
            if ($offset == 64) {
1804
                $md5->add($pwarray);
1805
                $keyoffset = $tocopy;
1806
                $tocopy = 5 - $tocopy;
1807
                $offset = 0;
1808
                continue;
1809
            }
1810
1811
            $keyoffset = 0;
1812
            $tocopy = 5;
1813
            for ($i = 0; $i < 16; ++$i) {
1814
                $pwarray[$offset + $i] = $docid[$i];
1815
            }
1816
            $offset += 16;
1817
        }
1818
1819
        $pwarray[16] = "\x80";
1820
        for ($i = 0; $i < 47; ++$i) {
1821
            $pwarray[17 + $i] = "\0";
1822
        }
1823
        $pwarray[56] = "\x80";
1824
        $pwarray[57] = "\x0a";
1825
1826
        $md5->add($pwarray);
1827
        $valContext = $md5->getContext();
1828
1829
        $key = $this->makeKey(0, $valContext);
1830
1831
        $salt = $key->RC4($salt_data);
1832
        $hashedsalt = $key->RC4($hashedsalt_data);
1833
1834
        $salt .= "\x80" . str_repeat("\0", 47);
1835
        $salt[56] = "\x80";
1836
1837
        $md5->reset();
1838
        $md5->add($salt);
1839
        $mdContext2 = $md5->getContext();
1840
1841
        return $mdContext2 == $hashedsalt;
1842
    }
1843
1844
    /**
1845
     * CODEPAGE.
1846
     *
1847
     * This record stores the text encoding used to write byte
1848
     * strings, stored as MS Windows code page identifier.
1849
     *
1850
     * --    "OpenOffice.org's Documentation of the Microsoft
1851
     *         Excel File Format"
1852
     */
1853 4 View Code Duplication
    private function readCodepage()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
2187
2188
                // bit: 29-23; mask: 0x3F800000; right color
2189 4
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2190
2191
                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
2192 4
                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
2193
2194
                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2195 4
                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2196
2197 4
                if ($diagonalUp == false && $diagonalDown == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
2198 4
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_NONE);
2199
                } 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...
2200
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_UP);
2201
                } 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...
2202
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_DOWN);
2203
                } 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...
2204
                    $objStyle->getBorders()->setDiagonalDirection(\PhpOffice\PhpSpreadsheet\Style\Borders::DIAGONAL_BOTH);
2205
                }
2206
2207
                // 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...
2208
                // bit: 6-0; mask: 0x0000007F; top color
2209 4
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2210
2211
                // bit: 13-7; mask: 0x00003F80; bottom color
2212 4
                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2213
2214
                // bit: 20-14; mask: 0x001FC000; diagonal color
2215 4
                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2216
2217
                // bit: 24-21; mask: 0x01E00000; diagonal style
2218 4 View Code Duplication
                if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2219 4
                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2220
                }
2221
2222
                // bit: 31-26; mask: 0xFC000000 fill pattern
2223 4
                if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2224 4
                    $objStyle->getFill()->setFillType($fillType);
2225
                }
2226
                // offset: 18; size: 2; pattern and background colour
2227
                // bit: 6-0; mask: 0x007F; color index for pattern color
2228 4
                $objStyle->getFill()->startcolorIndex = (0x007F & self::getInt2d($recordData, 18)) >> 0;
0 ignored issues
show
Bug introduced by
The property startcolorIndex does not seem to exist. Did you mean startColor?

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

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

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

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

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

Loading history...
2232
            } else {
2233
                // BIFF5
2234
2235
                // offset: 7; size: 1; Text orientation and flags
2236
                $orientationAndFlags = ord($recordData[7]);
2237
2238
                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
2239
                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2240
                switch ($xfOrientation) {
2241
                    case 0:
2242
                        $objStyle->getAlignment()->setTextRotation(0);
2243
                        break;
2244
                    case 1:
2245
                        $objStyle->getAlignment()->setTextRotation(-165);
2246
                        break;
2247
                    case 2:
2248
                        $objStyle->getAlignment()->setTextRotation(90);
2249
                        break;
2250
                    case 3:
2251
                        $objStyle->getAlignment()->setTextRotation(-90);
2252
                        break;
2253
                }
2254
2255
                // offset: 8; size: 4; cell border lines and background area
2256
                $borderAndBackground = self::getInt4d($recordData, 8);
2257
2258
                // bit: 6-0; mask: 0x0000007F; color index for pattern color
2259
                $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...
2260
2261
                // bit: 13-7; mask: 0x00003F80; color index for pattern background
2262
                $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...
2263
2264
                // bit: 21-16; mask: 0x003F0000; fill pattern
2265
                $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2266
2267
                // bit: 24-22; mask: 0x01C00000; bottom line style
2268
                $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2269
2270
                // bit: 31-25; mask: 0xFE000000; bottom line color
2271
                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2272
2273
                // offset: 12; size: 4; cell border lines
2274
                $borderLines = self::getInt4d($recordData, 12);
2275
2276
                // bit: 2-0; mask: 0x00000007; top line style
2277
                $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2278
2279
                // bit: 5-3; mask: 0x00000038; left line style
2280
                $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2281
2282
                // bit: 8-6; mask: 0x000001C0; right line style
2283
                $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2284
2285
                // bit: 15-9; mask: 0x0000FE00; top line color index
2286
                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2287
2288
                // bit: 22-16; mask: 0x007F0000; left line color index
2289
                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2290
2291
                // bit: 29-23; mask: 0x3F800000; right line color index
2292
                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2293
            }
2294
2295
            // add cellStyleXf or cellXf and update mapping
2296 4
            if ($isCellStyleXf) {
2297
                // we only read one style XF record which is always the first
2298 4
                if ($this->xfIndex == 0) {
2299 4
                    $this->spreadsheet->addCellStyleXf($objStyle);
2300 4
                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
2301
                }
2302
            } else {
2303
                // we read all cell XF records
2304 4
                $this->spreadsheet->addCellXf($objStyle);
2305 4
                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2306
            }
2307
2308
            // update XF index for when we read next record
2309 4
            ++$this->xfIndex;
2310
        }
2311 4
    }
2312
2313 3
    private function readXfExt()
2314
    {
2315 3
        $length = self::getInt2d($this->data, $this->pos + 2);
2316 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2317
2318
        // move stream pointer to next record
2319 3
        $this->pos += 4 + $length;
2320
2321 3
        if (!$this->readDataOnly) {
2322
            // offset: 0; size: 2; 0x087D = repeated header
2323
2324
            // offset: 2; size: 2
2325
2326
            // offset: 4; size: 8; not used
2327
2328
            // offset: 12; size: 2; record version
2329
2330
            // offset: 14; size: 2; index to XF record which this record modifies
2331 3
            $ixfe = self::getInt2d($recordData, 14);
2332
2333
            // offset: 16; size: 2; not used
2334
2335
            // offset: 18; size: 2; number of extension properties that follow
2336 3
            $cexts = self::getInt2d($recordData, 18);
0 ignored issues
show
Unused Code introduced by
$cexts is not used, you could remove the assignment.

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

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

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

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

Loading history...
2337
2338
            // start reading the actual extension data
2339 3
            $offset = 20;
2340 3
            while ($offset < $length) {
2341
                // extension type
2342 3
                $extType = self::getInt2d($recordData, $offset);
2343
2344
                // extension length
2345 3
                $cb = self::getInt2d($recordData, $offset + 2);
2346
2347
                // extension data
2348 3
                $extData = substr($recordData, $offset + 4, $cb);
2349
2350
                switch ($extType) {
2351 3 View Code Duplication
                    case 4:        // fill start color
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2352 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2353 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2354
2355 3
                        if ($xclfType == 2) {
2356 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2357
2358
                            // modify the relevant style property
2359 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2360 1
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2361 1
                                $fill->getStartColor()->setRGB($rgb);
2362 1
                                unset($fill->startcolorIndex); // normal color index does not apply, discard
2363
                            }
2364
                        }
2365 3
                        break;
2366 3 View Code Duplication
                    case 5:        // fill end color
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2367 1
                        $xclfType = self::getInt2d($extData, 0); // color type
2368 1
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2369
2370 1
                        if ($xclfType == 2) {
2371 1
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2372
2373
                            // modify the relevant style property
2374 1
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2375 1
                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2376 1
                                $fill->getEndColor()->setRGB($rgb);
2377 1
                                unset($fill->endcolorIndex); // normal color index does not apply, discard
2378
                            }
2379
                        }
2380 1
                        break;
2381 3 View Code Duplication
                    case 7:        // border color top
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2382 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2383 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2384
2385 3
                        if ($xclfType == 2) {
2386 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2387
2388
                            // modify the relevant style property
2389 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2390 1
                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2391 1
                                $top->getColor()->setRGB($rgb);
2392 1
                                unset($top->colorIndex); // normal color index does not apply, discard
2393
                            }
2394
                        }
2395 3
                        break;
2396 3 View Code Duplication
                    case 8:        // border color bottom
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2397 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2398 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2399
2400 3
                        if ($xclfType == 2) {
2401 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2402
2403
                            // modify the relevant style property
2404 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2405 1
                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2406 1
                                $bottom->getColor()->setRGB($rgb);
2407 1
                                unset($bottom->colorIndex); // normal color index does not apply, discard
2408
                            }
2409
                        }
2410 3
                        break;
2411 3 View Code Duplication
                    case 9:        // border color left
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2412 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2413 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2414
2415 3
                        if ($xclfType == 2) {
2416 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2417
2418
                            // modify the relevant style property
2419 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2420 1
                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2421 1
                                $left->getColor()->setRGB($rgb);
2422 1
                                unset($left->colorIndex); // normal color index does not apply, discard
2423
                            }
2424
                        }
2425 3
                        break;
2426 3 View Code Duplication
                    case 10:        // border color right
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2427 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2428 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2429
2430 3
                        if ($xclfType == 2) {
2431 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2432
2433
                            // modify the relevant style property
2434 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2435 1
                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2436 1
                                $right->getColor()->setRGB($rgb);
2437 1
                                unset($right->colorIndex); // normal color index does not apply, discard
2438
                            }
2439
                        }
2440 3
                        break;
2441 3 View Code Duplication
                    case 11:        // border color diagonal
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2442
                        $xclfType = self::getInt2d($extData, 0); // color type
2443
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2444
2445
                        if ($xclfType == 2) {
2446
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2447
2448
                            // modify the relevant style property
2449
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2450
                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2451
                                $diagonal->getColor()->setRGB($rgb);
2452
                                unset($diagonal->colorIndex); // normal color index does not apply, discard
2453
                            }
2454
                        }
2455
                        break;
2456 3 View Code Duplication
                    case 13:    // font color
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2457 3
                        $xclfType = self::getInt2d($extData, 0); // color type
2458 3
                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
2459
2460 3
                        if ($xclfType == 2) {
2461 3
                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2462
2463
                            // modify the relevant style property
2464 3
                            if (isset($this->mapCellXfIndex[$ixfe])) {
2465 1
                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2466 1
                                $font->getColor()->setRGB($rgb);
2467 1
                                unset($font->colorIndex); // normal color index does not apply, discard
2468
                            }
2469
                        }
2470 3
                        break;
2471
                }
2472
2473 3
                $offset += $cb;
2474
            }
2475
        }
2476 3
    }
2477
2478
    /**
2479
     * Read STYLE record.
2480
     */
2481 4
    private function readStyle()
2482
    {
2483 4
        $length = self::getInt2d($this->data, $this->pos + 2);
2484 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
2485
2486
        // move stream pointer to next record
2487 4
        $this->pos += 4 + $length;
2488
2489 4
        if (!$this->readDataOnly) {
2490
            // offset: 0; size: 2; index to XF record and flag for built-in style
2491 4
            $ixfe = self::getInt2d($recordData, 0);
2492
2493
            // bit: 11-0; mask 0x0FFF; index to XF record
2494 4
            $xfIndex = (0x0FFF & $ixfe) >> 0;
0 ignored issues
show
Unused Code introduced by
$xfIndex is not used, you could remove the assignment.

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

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

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

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

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

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

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

Loading history...
2583 4
            $string = self::readUnicodeStringShort(substr($recordData, 6));
2584 4
            $rec_name = $string['value'];
2585
        } elseif ($this->version == self::XLS_BIFF7) {
2586
            $string = $this->readByteStringShort(substr($recordData, 6));
2587
            $rec_name = $string['value'];
2588
        }
2589
2590 4
        $this->sheets[] = [
2591 4
            'name' => $rec_name,
0 ignored issues
show
Bug introduced by
The variable $rec_name does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

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

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

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

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

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
3156
    {
3157 4
        $length = self::getInt2d($this->data, $this->pos + 2);
3158 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3159
3160
        // move stream pointer to next record
3161 4
        $this->pos += 4 + $length;
3162
3163 4
        if (!$this->readDataOnly) {
3164
            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
3165 4
            $isHorizontalCentered = (bool) self::getInt2d($recordData, 0);
3166
3167 4
            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3168
        }
3169 4
    }
3170
3171
    /**
3172
     * Read VCENTER record.
3173
     */
3174 4 View Code Duplication
    private function readVcenter()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

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

Loading history...
3734
    {
3735 1
        $length = self::getInt2d($this->data, $this->pos + 2);
3736 1
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3737
3738
        // move stream pointer to next record
3739 1
        $this->pos += 4 + $length;
3740
3741
        // offset: 0; size: 2; index to row
3742 1
        $row = self::getInt2d($recordData, 0);
3743
3744
        // offset: 2; size 2; index to column
3745 1
        $column = self::getInt2d($recordData, 2);
3746 1
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3747
3748
        // Read cell?
3749 1
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3750
            // offset 4; size: 2; index to XF record
3751 1
            $xfIndex = self::getInt2d($recordData, 4);
3752
3753 1
            $numValue = self::extractNumber(substr($recordData, 6, 8));
3754
3755 1
            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
3756 1
            if (!$this->readDataOnly) {
3757
                // add cell style
3758 1
                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3759
            }
3760
3761
            // add cell value
3762 1
            $cell->setValueExplicit($numValue, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC);
3763
        }
3764 1
    }
3765
3766
    /**
3767
     * Read FORMULA record + perhaps a following STRING record if formula result is a string
3768
     * This record contains the token array and the result of a
3769
     * formula cell.
3770
     *
3771
     * --    "OpenOffice.org's Documentation of the Microsoft
3772
     *         Excel File Format"
3773
     */
3774 2
    private function readFormula()
3775
    {
3776 2
        $length = self::getInt2d($this->data, $this->pos + 2);
3777 2
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
3778
3779
        // move stream pointer to next record
3780 2
        $this->pos += 4 + $length;
3781
3782
        // offset: 0; size: 2; row index
3783 2
        $row = self::getInt2d($recordData, 0);
3784
3785
        // offset: 2; size: 2; col index
3786 2
        $column = self::getInt2d($recordData, 2);
3787 2
        $columnString = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($column);
3788
3789
        // offset: 20: size: variable; formula structure
3790 2
        $formulaStructure = substr($recordData, 20);
3791
3792
        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
3793 2
        $options = self::getInt2d($recordData, 14);
3794
3795
        // bit: 0; mask: 0x0001; 1 = recalculate always
3796
        // bit: 1; mask: 0x0002; 1 = calculate on open
3797
        // bit: 2; mask: 0x0008; 1 = part of a shared formula
3798 2
        $isPartOfSharedFormula = (bool) (0x0008 & $options);
3799
3800
        // WARNING:
3801
        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
3802
        // the formula data may be ordinary formula data, therefore we need to check
3803
        // explicitly for the tExp token (0x01)
3804 2
        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
3805
3806 2
        if ($isPartOfSharedFormula) {
3807
            // part of shared formula which means there will be a formula with a tExp token and nothing else
3808
            // get the base cell, grab tExp token
3809
            $baseRow = self::getInt2d($formulaStructure, 3);
3810
            $baseCol = self::getInt2d($formulaStructure, 5);
3811
            $this->_baseCell = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol) . ($baseRow + 1);
0 ignored issues
show
Bug introduced by
The property _baseCell does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
3812
        }
3813
3814
        // Read cell?
3815 2
        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
3816 2
            if ($isPartOfSharedFormula) {
3817
                // formula is added to this cell after the sheet has been read
3818
                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
3819
            }
3820
3821
            // offset: 16: size: 4; not used
3822
3823
            // offset: 4; size: 2; XF index
3824 2
            $xfIndex = self::getInt2d($recordData, 4);
3825
3826
            // offset: 6; size: 8; result of the formula
3827 2
            if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
3828
                // String formula. Result follows in appended STRING record
3829
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING;
3830
3831
                // read possible SHAREDFMLA record
3832
                $code = self::getInt2d($this->data, $this->pos);
3833
                if ($code == self::XLS_TYPE_SHAREDFMLA) {
3834
                    $this->readSharedFmla();
3835
                }
3836
3837
                // read STRING record
3838
                $value = $this->readString();
3839 2 View Code Duplication
            } elseif ((ord($recordData[6]) == 1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3840 2
                && (ord($recordData[12]) == 255)
3841 2
                && (ord($recordData[13]) == 255)) {
3842
                // Boolean formula. Result is in +2; 0=false, 1=true
3843
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
3844
                $value = (bool) ord($recordData[8]);
3845 2
            } elseif ((ord($recordData[6]) == 2)
3846 2
                && (ord($recordData[12]) == 255)
3847 2
                && (ord($recordData[13]) == 255)) {
3848
                // Error formula. Error code is in +2
3849
                $dataType = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
3850
                $value = Xls\ErrorCode::lookup(ord($recordData[8]));
3851 2 View Code Duplication
            } elseif ((ord($recordData[6]) == 3)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

Loading history...
4133
4134
        // get spliced record data
4135 3
        $splicedRecordData = $this->getSplicedRecordData();
4136 3
        $recordData = $splicedRecordData['recordData'];
4137
4138 3
        $this->drawingData .= $recordData;
4139 3
    }
4140
4141
    /**
4142
     * Read OBJ record.
4143
     */
4144 3
    private function readObj()
4145
    {
4146 3
        $length = self::getInt2d($this->data, $this->pos + 2);
4147 3
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4148
4149
        // move stream pointer to next record
4150 3
        $this->pos += 4 + $length;
4151
4152 3
        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4153
            return;
4154
        }
4155
4156
        // recordData consists of an array of subrecords looking like this:
4157
        //    ft: 2 bytes; ftCmo type (0x15)
4158
        //    cb: 2 bytes; size in bytes of ftCmo data
4159
        //    ot: 2 bytes; Object Type
4160
        //    id: 2 bytes; Object id number
4161
        //    grbit: 2 bytes; Option Flags
4162
        //    data: var; subrecord data
4163
4164
        // for now, we are just interested in the second subrecord containing the object type
4165 3
        $ftCmoType = self::getInt2d($recordData, 0);
4166 3
        $cbCmoSize = self::getInt2d($recordData, 2);
4167 3
        $otObjType = self::getInt2d($recordData, 4);
4168 3
        $idObjID = self::getInt2d($recordData, 6);
4169 3
        $grbitOpts = self::getInt2d($recordData, 6);
4170
4171 3
        $this->objs[] = [
4172 3
            'ftCmoType' => $ftCmoType,
4173 3
            'cbCmoSize' => $cbCmoSize,
4174 3
            'otObjType' => $otObjType,
4175 3
            'idObjID' => $idObjID,
4176 3
            'grbitOpts' => $grbitOpts,
4177
        ];
4178 3
        $this->textObjRef = $idObjID;
4179 3
    }
4180
4181
    /**
4182
     * Read WINDOW2 record.
4183
     */
4184 4
    private function readWindow2()
4185
    {
4186 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4187 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4188
4189
        // move stream pointer to next record
4190 4
        $this->pos += 4 + $length;
4191
4192
        // offset: 0; size: 2; option flags
4193 4
        $options = self::getInt2d($recordData, 0);
4194
4195
        // offset: 2; size: 2; index to first visible row
4196 4
        $firstVisibleRow = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$firstVisibleRow is not used, you could remove the assignment.

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

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

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

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

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

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

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

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4246 4
                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4247 4
                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4248
            }
4249
        }
4250 4
    }
4251
4252
    /**
4253
     * Read PLV Record(Created by Excel2007 or upper).
4254
     */
4255 4
    private function readPageLayoutView()
4256
    {
4257 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4258 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4259
4260
        // move stream pointer to next record
4261 4
        $this->pos += 4 + $length;
4262
4263
        // offset: 0; size: 2; rt
4264
        //->ignore
4265 4
        $rt = self::getInt2d($recordData, 0);
0 ignored issues
show
Unused Code introduced by
$rt is not used, you could remove the assignment.

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

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

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

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

Loading history...
4266
        // offset: 2; size: 2; grbitfr
4267
        //->ignore
4268 4
        $grbitFrt = self::getInt2d($recordData, 2);
0 ignored issues
show
Unused Code introduced by
$grbitFrt is not used, you could remove the assignment.

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

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

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

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

Loading history...
4269
        // offset: 4; size: 8; reserved
4270
        //->ignore
4271
4272
        // offset: 12; size 2; zoom scale
4273 4
        $wScalePLV = self::getInt2d($recordData, 12);
4274
        // offset: 14; size 2; grbit
4275 4
        $grbit = self::getInt2d($recordData, 14);
4276
4277
        // decomprise grbit
4278 4
        $fPageLayoutView = $grbit & 0x01;
4279 4
        $fRulerVisible = ($grbit >> 1) & 0x01; //no support
0 ignored issues
show
Unused Code introduced by
$fRulerVisible is not used, you could remove the assignment.

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

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

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

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

Loading history...
4280 4
        $fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
0 ignored issues
show
Unused Code introduced by
$fWhitespaceHidden is not used, you could remove the assignment.

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

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

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

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

Loading history...
4281
4282 4
        if ($fPageLayoutView === 1) {
4283
            $this->phpSheet->getSheetView()->setView(\PhpOffice\PhpSpreadsheet\Worksheet\SheetView::SHEETVIEW_PAGE_LAYOUT);
4284
            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
4285
        }
4286
        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
4287 4
    }
4288
4289
    /**
4290
     * Read SCL record.
4291
     */
4292
    private function readScl()
4293
    {
4294
        $length = self::getInt2d($this->data, $this->pos + 2);
4295
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4296
4297
        // move stream pointer to next record
4298
        $this->pos += 4 + $length;
4299
4300
        // offset: 0; size: 2; numerator of the view magnification
4301
        $numerator = self::getInt2d($recordData, 0);
4302
4303
        // offset: 2; size: 2; numerator of the view magnification
4304
        $denumerator = self::getInt2d($recordData, 2);
4305
4306
        // set the zoom scale (in percent)
4307
        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4308
    }
4309
4310
    /**
4311
     * Read PANE record.
4312
     */
4313
    private function readPane()
4314
    {
4315
        $length = self::getInt2d($this->data, $this->pos + 2);
4316
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4317
4318
        // move stream pointer to next record
4319
        $this->pos += 4 + $length;
4320
4321
        if (!$this->readDataOnly) {
4322
            // offset: 0; size: 2; position of vertical split
4323
            $px = self::getInt2d($recordData, 0);
4324
4325
            // offset: 2; size: 2; position of horizontal split
4326
            $py = self::getInt2d($recordData, 2);
4327
4328
            if ($this->frozen) {
4329
                // frozen panes
4330
                $this->phpSheet->freezePane(\PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($px) . ($py + 1));
4331
            }
4332
                // unfrozen panes; split windows; not supported by PhpSpreadsheet core
4333
        }
4334
    }
4335
4336
    /**
4337
     * Read SELECTION record. There is one such record for each pane in the sheet.
4338
     */
4339 4
    private function readSelection()
4340
    {
4341 4
        $length = self::getInt2d($this->data, $this->pos + 2);
4342 4
        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
4343
4344
        // move stream pointer to next record
4345 4
        $this->pos += 4 + $length;
4346
4347 4
        if (!$this->readDataOnly) {
4348
            // offset: 0; size: 1; pane identifier
4349 4
            $paneId = ord($recordData[0]);
0 ignored issues
show
Unused Code introduced by
$paneId is not used, you could remove the assignment.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
6572
                $name = 'tMemErr';
6573
                // offset: 1; size: 4; not used
6574
                // offset: 5; size: 2; size of the following subexpression
6575
                $subSize = self::getInt2d($formulaData, 5);
6576
                $size = 7 + $subSize;
6577
                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
6578
                break;
6579 1
            case 0x29:    //    Variable reference sub-expression
6580 1
            case 0x49:
6581 1 View Code Duplication
            case 0x69:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

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

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

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

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

Loading history...
6928
6929
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
6930 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...
6931
            // absolute column index
6932
            $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($lcIndex);
6933
            $lc = '$' . $lc;
6934
        } else {
6935
            // column offset
6936
            $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
6937
            $lc = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($baseCol + $lcIndex);
6938
        }
6939
6940
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
6941 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...
6942
            // absolute row index
6943
            $lr = $lrIndex + 1;
6944
            $lr = '$' . $lr;
6945
        } else {
6946
            // row offset
6947
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
6948
            $lr = $baseRow + $lrIndex;
6949
        }
6950
6951
        return "$fc$fr:$lc$lr";
6952
    }
6953
6954
    /**
6955
     * Read BIFF8 cell range address list
6956
     * section 2.5.15.
6957
     *
6958
     * @param string $subData
6959
     *
6960
     * @return array
6961
     */
6962 2 View Code Duplication
    private function readBIFF8CellRangeAddressList($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
6963
    {
6964 2
        $cellRangeAddresses = [];
6965
6966
        // offset: 0; size: 2; number of the following cell range addresses
6967 2
        $nm = self::getInt2d($subData, 0);
6968
6969 2
        $offset = 2;
6970
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
6971 2
        for ($i = 0; $i < $nm; ++$i) {
6972 2
            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
6973 2
            $offset += 8;
6974
        }
6975
6976
        return [
6977 2
            'size' => 2 + 8 * $nm,
6978 2
            'cellRangeAddresses' => $cellRangeAddresses,
6979
        ];
6980
    }
6981
6982
    /**
6983
     * Read BIFF5 cell range address list
6984
     * section 2.5.15.
6985
     *
6986
     * @param string $subData
6987
     *
6988
     * @return array
6989
     */
6990 4 View Code Duplication
    private function readBIFF5CellRangeAddressList($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
7241
7242
        // offset: 0: size: 1; length of the string (character count)
7243 4
        $characterCount = ord($subData[0]);
7244
7245 4
        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7246
7247
        // add 1 for the string length
7248 4
        $string['size'] += 1;
7249
7250 4
        return $string;
7251
    }
7252
7253
    /**
7254
     * Extracts an Excel Unicode long string (16-bit string length)
7255
     * OpenOffice documentation: 2.5.3
7256
     * this function is under construction, needs to support rich text, and Asian phonetic settings.
7257
     *
7258
     * @param string $subData
7259
     *
7260
     * @return array
7261
     */
7262 4 View Code Duplication
    private static function readUnicodeStringLong($subData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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