Failed Conditions
Pull Request — master (#4118)
by Owen
13:53
created

Biff8::readBIFF8CellRangeAddressB()   C

Complexity

Conditions 11
Paths 225

Size

Total Lines 75
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 37
c 2
b 0
f 0
dl 0
loc 75
rs 6.1708
cc 11
nc 225
nop 2

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
7
use PhpOffice\PhpSpreadsheet\Reader\Xls;
8
9
class Biff8 extends Xls
10
{
11
    /**
12
     * read BIFF8 constant value array from array data
13
     * returns e.g. ['value' => '{1,2;3,4}', 'size' => 40]
14
     * section 2.5.8.
15
     */
16
    protected static function readBIFF8ConstantArray(string $arrayData): array
17
    {
18
        // offset: 0; size: 1; number of columns decreased by 1
19
        $nc = ord($arrayData[0]);
20
21
        // offset: 1; size: 2; number of rows decreased by 1
22
        $nr = self::getUInt2d($arrayData, 1);
23
        $size = 3; // initialize
24
        $arrayData = substr($arrayData, 3);
25
26
        // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
27
        $matrixChunks = [];
28
        for ($r = 1; $r <= $nr + 1; ++$r) {
29
            $items = [];
30
            for ($c = 1; $c <= $nc + 1; ++$c) {
31
                $constant = self::readBIFF8Constant($arrayData);
32
                $items[] = $constant['value'];
33
                $arrayData = substr($arrayData, $constant['size']);
34
                $size += $constant['size'];
35
            }
36
            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
37
        }
38
        $matrix = '{' . implode(';', $matrixChunks) . '}';
39
40
        return [
41
            'value' => $matrix,
42
            'size' => $size,
43
        ];
44
    }
45
46
    /**
47
     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
48
     * section 2.5.7
49
     * returns e.g. ['value' => '5', 'size' => 9].
50
     */
51
    private static function readBIFF8Constant(string $valueData): array
52
    {
53
        // offset: 0; size: 1; identifier for type of constant
54
        $identifier = ord($valueData[0]);
55
56
        switch ($identifier) {
57
            case 0x00: // empty constant (what is this?)
58
                $value = '';
59
                $size = 9;
60
61
                break;
62
            case 0x01: // number
63
                // offset: 1; size: 8; IEEE 754 floating-point value
64
                $value = self::extractNumber(substr($valueData, 1, 8));
65
                $size = 9;
66
67
                break;
68
            case 0x02: // string value
69
                // offset: 1; size: var; Unicode string, 16-bit string length
70
                $string = self::readUnicodeStringLong(substr($valueData, 1));
71
                $value = '"' . $string['value'] . '"';
72
                $size = 1 + $string['size'];
73
74
                break;
75
            case 0x04: // boolean
76
                // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
77
                if (ord($valueData[1])) {
78
                    $value = 'TRUE';
79
                } else {
80
                    $value = 'FALSE';
81
                }
82
                $size = 9;
83
84
                break;
85
            case 0x10: // error code
86
                // offset: 1; size: 1; error code
87
                $value = ErrorCode::lookup(ord($valueData[1]));
88
                $size = 9;
89
90
                break;
91
            default:
92
                throw new ReaderException('Unsupported BIFF8 constant');
93
        }
94
95
        return [
96
            'value' => $value,
97
            'size' => $size,
98
        ];
99
    }
100
101
    /**
102
     * Read BIFF8 cell range address list
103
     * section 2.5.15.
104
     */
105
    public static function readBIFF8CellRangeAddressList(string $subData): array
106
    {
107
        $cellRangeAddresses = [];
108
109
        // offset: 0; size: 2; number of the following cell range addresses
110
        $nm = self::getUInt2d($subData, 0);
111
112
        $offset = 2;
113
        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
114
        for ($i = 0; $i < $nm; ++$i) {
115
            $cellRangeAddresses[] = self::readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
116
            $offset += 8;
117
        }
118
119
        return [
120
            'size' => 2 + 8 * $nm,
121
            'cellRangeAddresses' => $cellRangeAddresses,
122
        ];
123
    }
124
125
    /**
126
     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
127
     * section 3.3.4.
128
     */
129
    protected static function readBIFF8CellAddress(string $cellAddressStructure): string
130
    {
131
        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
132
        $row = self::getUInt2d($cellAddressStructure, 0) + 1;
133
134
        // offset: 2; size: 2; index to column or column offset + relative flags
135
        // bit: 7-0; mask 0x00FF; column index
136
        $column = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($cellAddressStructure, 2)) + 1);
137
138
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
139
        if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
140
            $column = '$' . $column;
141
        }
142
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
143
        if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
144
            $row = '$' . $row;
145
        }
146
147
        return $column . $row;
148
    }
149
150
    /**
151
     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
152
     * to indicate offsets from a base cell
153
     * section 3.3.4.
154
     *
155
     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
156
     */
157
    protected static function readBIFF8CellAddressB(string $cellAddressStructure, string $baseCell = 'A1'): string
158
    {
159
        [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
160
        $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
161
        $baseRow = (int) $baseRow;
162
163
        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
164
        $rowIndex = self::getUInt2d($cellAddressStructure, 0);
165
        $row = self::getUInt2d($cellAddressStructure, 0) + 1;
166
167
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
168
        if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
169
            // offset: 2; size: 2; index to column or column offset + relative flags
170
            // bit: 7-0; mask 0x00FF; column index
171
            $colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2);
172
173
            $column = Coordinate::stringFromColumnIndex($colIndex + 1);
174
            $column = '$' . $column;
175
        } else {
176
            // offset: 2; size: 2; index to column or column offset + relative flags
177
            // bit: 7-0; mask 0x00FF; column index
178
            $relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
179
            $colIndex = $baseCol + $relativeColIndex;
180
            $colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256;
181
            $colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256;
182
            $column = Coordinate::stringFromColumnIndex($colIndex + 1);
183
        }
184
185
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
186
        if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
187
            $row = '$' . $row;
188
        } else {
189
            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
190
            $row = $baseRow + $rowIndex;
191
        }
192
193
        return $column . $row;
194
    }
195
196
    /**
197
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
198
     * always fixed range
199
     * section 2.5.14.
200
     */
201
    protected static function readBIFF8CellRangeAddressFixed(string $subData): string
202
    {
203
        // offset: 0; size: 2; index to first row
204
        $fr = self::getUInt2d($subData, 0) + 1;
205
206
        // offset: 2; size: 2; index to last row
207
        $lr = self::getUInt2d($subData, 2) + 1;
208
209
        // offset: 4; size: 2; index to first column
210
        $fc = self::getUInt2d($subData, 4);
211
212
        // offset: 6; size: 2; index to last column
213
        $lc = self::getUInt2d($subData, 6);
214
215
        // check values
216
        if ($fr > $lr || $fc > $lc) {
217
            throw new ReaderException('Not a cell range address');
218
        }
219
220
        // column index to letter
221
        $fc = Coordinate::stringFromColumnIndex($fc + 1);
222
        $lc = Coordinate::stringFromColumnIndex($lc + 1);
223
224
        if ($fr == $lr && $fc == $lc) {
225
            return "$fc$fr";
226
        }
227
228
        return "$fc$fr:$lc$lr";
229
    }
230
231
    /**
232
     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
233
     * there are flags indicating whether column/row index is relative
234
     * section 3.3.4.
235
     */
236
    protected static function readBIFF8CellRangeAddress(string $subData): string
237
    {
238
        // todo: if cell range is just a single cell, should this funciton
239
        // not just return e.g. 'A1' and not 'A1:A1' ?
240
241
        // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
242
        $fr = self::getUInt2d($subData, 0) + 1;
243
244
        // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
245
        $lr = self::getUInt2d($subData, 2) + 1;
246
247
        // offset: 4; size: 2; index to first column or column offset + relative flags
248
249
        // bit: 7-0; mask 0x00FF; column index
250
        $fc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 4)) + 1);
251
252
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
253
        if (!(0x4000 & self::getUInt2d($subData, 4))) {
254
            $fc = '$' . $fc;
255
        }
256
257
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
258
        if (!(0x8000 & self::getUInt2d($subData, 4))) {
259
            $fr = '$' . $fr;
260
        }
261
262
        // offset: 6; size: 2; index to last column or column offset + relative flags
263
264
        // bit: 7-0; mask 0x00FF; column index
265
        $lc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 6)) + 1);
266
267
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
268
        if (!(0x4000 & self::getUInt2d($subData, 6))) {
269
            $lc = '$' . $lc;
270
        }
271
272
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
273
        if (!(0x8000 & self::getUInt2d($subData, 6))) {
274
            $lr = '$' . $lr;
275
        }
276
277
        return "$fc$fr:$lc$lr";
278
    }
279
280
    /**
281
     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
282
     * to indicate offsets from a base cell
283
     * section 3.3.4.
284
     *
285
     * @param string $baseCell Base cell
286
     *
287
     * @return string Cell range address
288
     */
289
    protected static function readBIFF8CellRangeAddressB(string $subData, string $baseCell = 'A1'): string
290
    {
291
        [$baseCol, $baseRow] = Coordinate::indexesFromString($baseCell);
292
        $baseCol = $baseCol - 1;
293
294
        // TODO: if cell range is just a single cell, should this funciton
295
        // not just return e.g. 'A1' and not 'A1:A1' ?
296
297
        // offset: 0; size: 2; first row
298
        $frIndex = self::getUInt2d($subData, 0); // adjust below
299
300
        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
301
        $lrIndex = self::getUInt2d($subData, 2); // adjust below
302
303
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
304
        if (!(0x4000 & self::getUInt2d($subData, 4))) {
305
            // absolute column index
306
            // offset: 4; size: 2; first column with relative/absolute flags
307
            // bit: 7-0; mask 0x00FF; column index
308
            $fcIndex = 0x00FF & self::getUInt2d($subData, 4);
309
            $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
310
            $fc = '$' . $fc;
311
        } else {
312
            // column offset
313
            // offset: 4; size: 2; first column with relative/absolute flags
314
            // bit: 7-0; mask 0x00FF; column index
315
            $relativeFcIndex = 0x00FF & self::getInt2d($subData, 4);
316
            $fcIndex = $baseCol + $relativeFcIndex;
317
            $fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256;
318
            $fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256;
319
            $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
320
        }
321
322
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
323
        if (!(0x8000 & self::getUInt2d($subData, 4))) {
324
            // absolute row index
325
            $fr = $frIndex + 1;
326
            $fr = '$' . $fr;
327
        } else {
328
            // row offset
329
            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
330
            $fr = $baseRow + $frIndex;
331
        }
332
333
        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
334
        if (!(0x4000 & self::getUInt2d($subData, 6))) {
335
            // absolute column index
336
            // offset: 6; size: 2; last column with relative/absolute flags
337
            // bit: 7-0; mask 0x00FF; column index
338
            $lcIndex = 0x00FF & self::getUInt2d($subData, 6);
339
            $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
340
            $lc = '$' . $lc;
341
        } else {
342
            // column offset
343
            // offset: 6; size: 2; last column with relative/absolute flags
344
            // bit: 7-0; mask 0x00FF; column index
345
            $relativeLcIndex = 0x00FF & self::getInt2d($subData, 6);
346
            $lcIndex = $baseCol + $relativeLcIndex;
347
            $lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256;
348
            $lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256;
349
            $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
350
        }
351
352
        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
353
        if (!(0x8000 & self::getUInt2d($subData, 6))) {
354
            // absolute row index
355
            $lr = $lrIndex + 1;
356
            $lr = '$' . $lr;
357
        } else {
358
            // row offset
359
            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
360
            $lr = $baseRow + $lrIndex;
361
        }
362
363
        return "$fc$fr:$lc$lr";
364
    }
365
}
366