Completed
Push — develop ( 268fc1...a06731 )
by Adrien
23:59
created

Calculation   F

Complexity

Total Complexity 526

Size/Duplication

Total Lines 4145
Duplicated Lines 6.39 %

Coupling/Cohesion

Components 2
Dependencies 12

Test Coverage

Coverage 55.17%

Importance

Changes 0
Metric Value
dl 265
loc 4145
ccs 614
cts 1113
cp 0.5517
rs 3.4838
c 0
b 0
f 0
wmc 526
lcom 2
cbo 12

55 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A loadLocales() 0 10 3
B getInstance() 0 15 5
A __destruct() 0 4 1
A flushInstance() 0 4 1
A getDebugLog() 0 4 1
A __clone() 0 4 1
A getTRUE() 0 4 1
A getFALSE() 0 4 1
A setArrayReturnType() 0 12 4
A getArrayReturnType() 0 4 1
A getCalculationCacheEnabled() 0 4 1
A setCalculationCacheEnabled() 0 5 1
A enableCalculationCache() 0 4 1
A disableCalculationCache() 0 4 1
A clearCalculationCache() 0 4 1
A clearCalculationCacheForWorksheet() 0 6 2
A renameCalculationCacheForWorksheet() 0 7 2
A getLocale() 0 4 1
D setLocale() 0 79 19
B translateSeparator() 0 21 6
B translateFormula() 9 30 5
C _translateFormulaToLocale() 24 24 7
C _translateFormulaToEnglish() 24 24 7
A localeFunc() 0 15 4
B wrapResult() 0 17 6
B unwrapResult() 0 13 8
A calculate() 0 8 2
C calculateCellValue() 0 66 15
A parseFormula() 0 16 4
B calculateFormula() 0 31 6
A getValueFromCache() 0 15 3
A saveValueToCache() 0 6 2
C _calculateFormulaValue() 0 56 15
C checkMatrixOperands() 0 30 7
A getMatrixDimensions() 0 16 3
C resizeMatricesShrink() 28 32 15
C resizeMatricesExtend() 32 36 15
D showValue() 0 30 10
D showTypeDetails() 0 31 10
C convertMatrixReferences() 0 50 9
A mkMatrix() 0 4 1
F _parseFormula() 44 311 113
A dataTestReference() 0 15 4
F processTokenStack() 36 365 102
C validateBinaryOperand() 0 37 11
F executeBinaryComparisonOperation() 40 121 42
A strcmpLowercaseFirst() 0 7 1
C executeNumericBinaryOperation() 0 74 19
A raiseFormulaError() 0 9 2
C extractCellRange() 14 41 7
C extractNamedRange() 14 56 10
A isImplemented() 0 9 2
A getFunctions() 0 4 1
A getImplementedFunctionNames() 0 11 3

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 Calculation 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 Calculation, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Category;
6
7 61
if (!defined('CALCULATION_REGEXP_CELLREF')) {
8
    //    Test for support of \P (multibyte options) in PCRE
9 61
    if (defined('PREG_BAD_UTF8_ERROR')) {
10
        //    Cell reference (cell or range of cells, with or without a sheet reference)
11 61
        define('CALCULATION_REGEXP_CELLREF', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})');
12
        //    Named Range of cells
13 61
        define('CALCULATION_REGEXP_NAMEDRANGE', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)');
14
    } else {
15
        //    Cell reference (cell or range of cells, with or without a sheet reference)
16
        define('CALCULATION_REGEXP_CELLREF', '(((\w*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d+)');
17
        //    Named Range of cells
18
        define('CALCULATION_REGEXP_NAMEDRANGE', '(((\w*)|(\'.*\')|(\".*\"))!)?([_A-Z][_A-Z0-9\.]*)');
19
    }
20
}
21
22
/**
23
 * Copyright (c) 2006 - 2016 PhpSpreadsheet
24
 *
25
 * This library is free software; you can redistribute it and/or
26
 * modify it under the terms of the GNU Lesser General Public
27
 * License as published by the Free Software Foundation; either
28
 * version 2.1 of the License, or (at your option) any later version.
29
 *
30
 * This library is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
33
 * Lesser General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU Lesser General Public
36
 * License along with this library; if not, write to the Free Software
37
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
38
 *
39
 * @category   PhpSpreadsheet
40
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
41
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
42
 */
43
class Calculation
44
{
45
    /** Constants                */
46
/** Regular Expressions        */
47
    //    Numeric operand
48
    const CALCULATION_REGEXP_NUMBER = '[-+]?\d*\.?\d+(e[-+]?\d+)?';
49
    //    String operand
50
    const CALCULATION_REGEXP_STRING = '"(?:[^"]|"")*"';
51
    //    Opening bracket
52
    const CALCULATION_REGEXP_OPENBRACE = '\(';
53
    //    Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
54
    const CALCULATION_REGEXP_FUNCTION = '@?([A-Z][A-Z0-9\.]*)[\s]*\(';
55
    //    Cell reference (cell or range of cells, with or without a sheet reference)
56
    const CALCULATION_REGEXP_CELLREF = CALCULATION_REGEXP_CELLREF;
57
    //    Named Range of cells
58
    const CALCULATION_REGEXP_NAMEDRANGE = CALCULATION_REGEXP_NAMEDRANGE;
59
    //    Error
60
    const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?';
61
62
    /** constants */
63
    const RETURN_ARRAY_AS_ERROR = 'error';
64
    const RETURN_ARRAY_AS_VALUE = 'value';
65
    const RETURN_ARRAY_AS_ARRAY = 'array';
66
67
    private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
68
69
    /**
70
     * Instance of this class
71
     *
72
     * @var \PhpOffice\PhpSpreadsheet\Calculation
73
     */
74
    private static $instance;
75
76
    /**
77
     * Instance of the spreadsheet this Calculation Engine is using
78
     *
79
     * @var PhpSpreadsheet
80
     */
81
    private $spreadsheet;
82
83
    /**
84
     * List of instances of the calculation engine that we've instantiated for individual spreadsheets
85
     *
86
     * @var \PhpOffice\PhpSpreadsheet\Calculation[]
87
     */
88
    private static $spreadsheetSets;
0 ignored issues
show
Unused Code introduced by
The property $spreadsheetSets 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...
89
90
    /**
91
     * Calculation cache
92
     *
93
     * @var array
94
     */
95
    private $calculationCache = [];
96
97
    /**
98
     * Calculation cache enabled
99
     *
100
     * @var bool
101
     */
102
    private $calculationCacheEnabled = true;
103
104
    /**
105
     * List of operators that can be used within formulae
106
     * The true/false value indicates whether it is a binary operator or a unary operator
107
     *
108
     * @var array
109
     */
110
    private static $operators = [
111
        '+' => true, '-' => true, '*' => true, '/' => true,
112
        '^' => true, '&' => true, '%' => false, '~' => false,
113
        '>' => true, '<' => true, '=' => true, '>=' => true,
114
        '<=' => true, '<>' => true, '|' => true, ':' => true,
115
    ];
116
117
    /**
118
     * List of binary operators (those that expect two operands)
119
     *
120
     * @var array
121
     */
122
    private static $binaryOperators = [
123
        '+' => true, '-' => true, '*' => true, '/' => true,
124
        '^' => true, '&' => true, '>' => true, '<' => true,
125
        '=' => true, '>=' => true, '<=' => true, '<>' => true,
126
        '|' => true, ':' => true,
127
    ];
128
129
    /**
130
     * The debug log generated by the calculation engine
131
     *
132
     * @var CalcEngine\Logger
133
     */
134
    private $debugLog;
0 ignored issues
show
Unused Code introduced by
The property $debugLog 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...
135
136
    /**
137
     * Flag to determine how formula errors should be handled
138
     *        If true, then a user error will be triggered
139
     *        If false, then an exception will be thrown
140
     *
141
     * @var bool
142
     */
143
    public $suppressFormulaErrors = false;
144
145
    /**
146
     * Error message for any error that was raised/thrown by the calculation engine
147
     *
148
     * @var string
149
     */
150
    public $formulaError = null;
151
152
    /**
153
     * An array of the nested cell references accessed by the calculation engine, used for the debug log
154
     *
155
     * @var array of string
156
     */
157
    private $cyclicReferenceStack;
158
159
    private $cellStack = [];
160
161
    /**
162
     * Current iteration counter for cyclic formulae
163
     * If the value is 0 (or less) then cyclic formulae will throw an exception,
164
     *    otherwise they will iterate to the limit defined here before returning a result
165
     *
166
     * @var int
167
     */
168
    private $cyclicFormulaCounter = 1;
169
170
    private $cyclicFormulaCell = '';
171
172
    /**
173
     * Number of iterations for cyclic formulae
174
     *
175
     * @var int
176
     */
177
    public $cyclicFormulaCount = 1;
178
179
    /**
180
     * Epsilon Precision used for comparisons in calculations
181
     *
182
     * @var float
183
     */
184
    private $delta = 0.1e-12;
185
186
    /**
187
     * The current locale setting
188
     *
189
     * @var string
190
     */
191
    private static $localeLanguage = 'en_us'; //    US English    (default locale)
192
193
    /**
194
     * List of available locale settings
195
     * Note that this is read for the locale subdirectory only when requested
196
     *
197
     * @var string[]
198
     */
199
    private static $validLocaleLanguages = [
200
        'en', //    English        (default language)
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...
201
    ];
202
203
    /**
204
     * Locale-specific argument separator for function arguments
205
     *
206
     * @var string
207
     */
208
    private static $localeArgumentSeparator = ',';
209
    private static $localeFunctions = [];
210
211
    /**
212
     * Locale-specific translations for Excel constants (True, False and Null)
213
     *
214
     * @var string[]
215
     */
216
    public static $localeBoolean = [
217
        'TRUE' => 'TRUE',
218
        'FALSE' => 'FALSE',
219
        'NULL' => 'NULL',
220
    ];
221
222
    /**
223
     * Excel constant string translations to their PHP equivalents
224
     * Constant conversion from text name/value to actual (datatyped) value
225
     *
226
     * @var string[]
227
     */
228
    private static $excelConstants = [
229
        'TRUE' => true,
230
        'FALSE' => false,
231
        'NULL' => null,
232
    ];
233
234
    // PhpSpreadsheet functions
235
    private static $phpSpreadsheetFunctions = [
236
        'ABS' => [
237
            'category' => Category::CATEGORY_MATH_AND_TRIG,
238
            'functionCall' => 'abs',
239
            'argumentCount' => '1',
240
        ],
241
        'ACCRINT' => [
242
            'category' => Category::CATEGORY_FINANCIAL,
243
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::ACCRINT',
244
            'argumentCount' => '4-7',
245
        ],
246
        'ACCRINTM' => [
247
            'category' => Category::CATEGORY_FINANCIAL,
248
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::ACCRINTM',
249
            'argumentCount' => '3-5',
250
        ],
251
        'ACOS' => [
252
            'category' => Category::CATEGORY_MATH_AND_TRIG,
253
            'functionCall' => 'acos',
254
            'argumentCount' => '1',
255
        ],
256
        'ACOSH' => [
257
            'category' => Category::CATEGORY_MATH_AND_TRIG,
258
            'functionCall' => 'acosh',
259
            'argumentCount' => '1',
260
        ],
261
        'ADDRESS' => [
262
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
263
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::cellAddress',
264
            'argumentCount' => '2-5',
265
        ],
266
        'AMORDEGRC' => [
267
            'category' => Category::CATEGORY_FINANCIAL,
268
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::AMORDEGRC',
269
            'argumentCount' => '6,7',
270
        ],
271
        'AMORLINC' => [
272
            'category' => Category::CATEGORY_FINANCIAL,
273
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::AMORLINC',
274
            'argumentCount' => '6,7',
275
        ],
276
        'AND' => [
277
            'category' => Category::CATEGORY_LOGICAL,
278
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::logicalAnd',
279
            'argumentCount' => '1+',
280
        ],
281
        'AREAS' => [
282
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
283
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
284
            'argumentCount' => '1',
285
        ],
286
        'ASC' => [
287
            'category' => Category::CATEGORY_TEXT_AND_DATA,
288
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
289
            'argumentCount' => '1',
290
        ],
291
        'ASIN' => [
292
            'category' => Category::CATEGORY_MATH_AND_TRIG,
293
            'functionCall' => 'asin',
294
            'argumentCount' => '1',
295
        ],
296
        'ASINH' => [
297
            'category' => Category::CATEGORY_MATH_AND_TRIG,
298
            'functionCall' => 'asinh',
299
            'argumentCount' => '1',
300
        ],
301
        'ATAN' => [
302
            'category' => Category::CATEGORY_MATH_AND_TRIG,
303
            'functionCall' => 'atan',
304
            'argumentCount' => '1',
305
        ],
306
        'ATAN2' => [
307
            'category' => Category::CATEGORY_MATH_AND_TRIG,
308
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::ATAN2',
309
            'argumentCount' => '2',
310
        ],
311
        'ATANH' => [
312
            'category' => Category::CATEGORY_MATH_AND_TRIG,
313
            'functionCall' => 'atanh',
314
            'argumentCount' => '1',
315
        ],
316
        'AVEDEV' => [
317
            'category' => Category::CATEGORY_STATISTICAL,
318
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::AVEDEV',
319
            'argumentCount' => '1+',
320
        ],
321
        'AVERAGE' => [
322
            'category' => Category::CATEGORY_STATISTICAL,
323
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::AVERAGE',
324
            'argumentCount' => '1+',
325
        ],
326
        'AVERAGEA' => [
327
            'category' => Category::CATEGORY_STATISTICAL,
328
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::AVERAGEA',
329
            'argumentCount' => '1+',
330
        ],
331
        'AVERAGEIF' => [
332
            'category' => Category::CATEGORY_STATISTICAL,
333
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::AVERAGEIF',
334
            'argumentCount' => '2,3',
335
        ],
336
        'AVERAGEIFS' => [
337
            'category' => Category::CATEGORY_STATISTICAL,
338
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
339
            'argumentCount' => '3+',
340
        ],
341
        'BAHTTEXT' => [
342
            'category' => Category::CATEGORY_TEXT_AND_DATA,
343
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
344
            'argumentCount' => '1',
345
        ],
346
        'BESSELI' => [
347
            'category' => Category::CATEGORY_ENGINEERING,
348
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BESSELI',
349
            'argumentCount' => '2',
350
        ],
351
        'BESSELJ' => [
352
            'category' => Category::CATEGORY_ENGINEERING,
353
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BESSELJ',
354
            'argumentCount' => '2',
355
        ],
356
        'BESSELK' => [
357
            'category' => Category::CATEGORY_ENGINEERING,
358
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BESSELK',
359
            'argumentCount' => '2',
360
        ],
361
        'BESSELY' => [
362
            'category' => Category::CATEGORY_ENGINEERING,
363
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BESSELY',
364
            'argumentCount' => '2',
365
        ],
366
        'BETADIST' => [
367
            'category' => Category::CATEGORY_STATISTICAL,
368
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::BETADIST',
369
            'argumentCount' => '3-5',
370
        ],
371
        'BETAINV' => [
372
            'category' => Category::CATEGORY_STATISTICAL,
373
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::BETAINV',
374
            'argumentCount' => '3-5',
375
        ],
376
        'BIN2DEC' => [
377
            'category' => Category::CATEGORY_ENGINEERING,
378
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BINTODEC',
379
            'argumentCount' => '1',
380
        ],
381
        'BIN2HEX' => [
382
            'category' => Category::CATEGORY_ENGINEERING,
383
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BINTOHEX',
384
            'argumentCount' => '1,2',
385
        ],
386
        'BIN2OCT' => [
387
            'category' => Category::CATEGORY_ENGINEERING,
388
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::BINTOOCT',
389
            'argumentCount' => '1,2',
390
        ],
391
        'BINOMDIST' => [
392
            'category' => Category::CATEGORY_STATISTICAL,
393
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::BINOMDIST',
394
            'argumentCount' => '4',
395
        ],
396
        'CEILING' => [
397
            'category' => Category::CATEGORY_MATH_AND_TRIG,
398
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::CEILING',
399
            'argumentCount' => '2',
400
        ],
401
        'CELL' => [
402
            'category' => Category::CATEGORY_INFORMATION,
403
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
404
            'argumentCount' => '1,2',
405
        ],
406
        'CHAR' => [
407
            'category' => Category::CATEGORY_TEXT_AND_DATA,
408
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::CHARACTER',
409
            'argumentCount' => '1',
410
        ],
411
        'CHIDIST' => [
412
            'category' => Category::CATEGORY_STATISTICAL,
413
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::CHIDIST',
414
            'argumentCount' => '2',
415
        ],
416
        'CHIINV' => [
417
            'category' => Category::CATEGORY_STATISTICAL,
418
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::CHIINV',
419
            'argumentCount' => '2',
420
        ],
421
        'CHITEST' => [
422
            'category' => Category::CATEGORY_STATISTICAL,
423
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
424
            'argumentCount' => '2',
425
        ],
426
        'CHOOSE' => [
427
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
428
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::CHOOSE',
429
            'argumentCount' => '2+',
430
        ],
431
        'CLEAN' => [
432
            'category' => Category::CATEGORY_TEXT_AND_DATA,
433
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::TRIMNONPRINTABLE',
434
            'argumentCount' => '1',
435
        ],
436
        'CODE' => [
437
            'category' => Category::CATEGORY_TEXT_AND_DATA,
438
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::ASCIICODE',
439
            'argumentCount' => '1',
440
        ],
441
        'COLUMN' => [
442
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
443
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::COLUMN',
444
            'argumentCount' => '-1',
445
            'passByReference' => [true],
446
        ],
447
        'COLUMNS' => [
448
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
449
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::COLUMNS',
450
            'argumentCount' => '1',
451
        ],
452
        'COMBIN' => [
453
            'category' => Category::CATEGORY_MATH_AND_TRIG,
454
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::COMBIN',
455
            'argumentCount' => '2',
456
        ],
457
        'COMPLEX' => [
458
            'category' => Category::CATEGORY_ENGINEERING,
459
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::COMPLEX',
460
            'argumentCount' => '2,3',
461
        ],
462
        'CONCATENATE' => [
463
            'category' => Category::CATEGORY_TEXT_AND_DATA,
464
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::CONCATENATE',
465
            'argumentCount' => '1+',
466
        ],
467
        'CONFIDENCE' => [
468
            'category' => Category::CATEGORY_STATISTICAL,
469
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::CONFIDENCE',
470
            'argumentCount' => '3',
471
        ],
472
        'CONVERT' => [
473
            'category' => Category::CATEGORY_ENGINEERING,
474
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::CONVERTUOM',
475
            'argumentCount' => '3',
476
        ],
477
        'CORREL' => [
478
            'category' => Category::CATEGORY_STATISTICAL,
479
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::CORREL',
480
            'argumentCount' => '2',
481
        ],
482
        'COS' => [
483
            'category' => Category::CATEGORY_MATH_AND_TRIG,
484
            'functionCall' => 'cos',
485
            'argumentCount' => '1',
486
        ],
487
        'COSH' => [
488
            'category' => Category::CATEGORY_MATH_AND_TRIG,
489
            'functionCall' => 'cosh',
490
            'argumentCount' => '1',
491
        ],
492
        'COUNT' => [
493
            'category' => Category::CATEGORY_STATISTICAL,
494
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::COUNT',
495
            'argumentCount' => '1+',
496
        ],
497
        'COUNTA' => [
498
            'category' => Category::CATEGORY_STATISTICAL,
499
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::COUNTA',
500
            'argumentCount' => '1+',
501
        ],
502
        'COUNTBLANK' => [
503
            'category' => Category::CATEGORY_STATISTICAL,
504
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::COUNTBLANK',
505
            'argumentCount' => '1',
506
        ],
507
        'COUNTIF' => [
508
            'category' => Category::CATEGORY_STATISTICAL,
509
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::COUNTIF',
510
            'argumentCount' => '2',
511
        ],
512
        'COUNTIFS' => [
513
            'category' => Category::CATEGORY_STATISTICAL,
514
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
515
            'argumentCount' => '2',
516
        ],
517
        'COUPDAYBS' => [
518
            'category' => Category::CATEGORY_FINANCIAL,
519
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::COUPDAYBS',
520
            'argumentCount' => '3,4',
521
        ],
522
        'COUPDAYS' => [
523
            'category' => Category::CATEGORY_FINANCIAL,
524
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::COUPDAYS',
525
            'argumentCount' => '3,4',
526
        ],
527
        'COUPDAYSNC' => [
528
            'category' => Category::CATEGORY_FINANCIAL,
529
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::COUPDAYSNC',
530
            'argumentCount' => '3,4',
531
        ],
532
        'COUPNCD' => [
533
            'category' => Category::CATEGORY_FINANCIAL,
534
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::COUPNCD',
535
            'argumentCount' => '3,4',
536
        ],
537
        'COUPNUM' => [
538
            'category' => Category::CATEGORY_FINANCIAL,
539
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::COUPNUM',
540
            'argumentCount' => '3,4',
541
        ],
542
        'COUPPCD' => [
543
            'category' => Category::CATEGORY_FINANCIAL,
544
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::COUPPCD',
545
            'argumentCount' => '3,4',
546
        ],
547
        'COVAR' => [
548
            'category' => Category::CATEGORY_STATISTICAL,
549
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::COVAR',
550
            'argumentCount' => '2',
551
        ],
552
        'CRITBINOM' => [
553
            'category' => Category::CATEGORY_STATISTICAL,
554
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::CRITBINOM',
555
            'argumentCount' => '3',
556
        ],
557
        'CUBEKPIMEMBER' => [
558
            'category' => Category::CATEGORY_CUBE,
559
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
560
            'argumentCount' => '?',
561
        ],
562
        'CUBEMEMBER' => [
563
            'category' => Category::CATEGORY_CUBE,
564
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
565
            'argumentCount' => '?',
566
        ],
567
        'CUBEMEMBERPROPERTY' => [
568
            'category' => Category::CATEGORY_CUBE,
569
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
570
            'argumentCount' => '?',
571
        ],
572
        'CUBERANKEDMEMBER' => [
573
            'category' => Category::CATEGORY_CUBE,
574
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
575
            'argumentCount' => '?',
576
        ],
577
        'CUBESET' => [
578
            'category' => Category::CATEGORY_CUBE,
579
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
580
            'argumentCount' => '?',
581
        ],
582
        'CUBESETCOUNT' => [
583
            'category' => Category::CATEGORY_CUBE,
584
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
585
            'argumentCount' => '?',
586
        ],
587
        'CUBEVALUE' => [
588
            'category' => Category::CATEGORY_CUBE,
589
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
590
            'argumentCount' => '?',
591
        ],
592
        'CUMIPMT' => [
593
            'category' => Category::CATEGORY_FINANCIAL,
594
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::CUMIPMT',
595
            'argumentCount' => '6',
596
        ],
597
        'CUMPRINC' => [
598
            'category' => Category::CATEGORY_FINANCIAL,
599
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::CUMPRINC',
600
            'argumentCount' => '6',
601
        ],
602
        'DATE' => [
603
            'category' => Category::CATEGORY_DATE_AND_TIME,
604
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DATE',
605
            'argumentCount' => '3',
606
        ],
607
        'DATEDIF' => [
608
            'category' => Category::CATEGORY_DATE_AND_TIME,
609
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DATEDIF',
610
            'argumentCount' => '2,3',
611
        ],
612
        'DATEVALUE' => [
613
            'category' => Category::CATEGORY_DATE_AND_TIME,
614
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DATEVALUE',
615
            'argumentCount' => '1',
616
        ],
617
        'DAVERAGE' => [
618
            'category' => Category::CATEGORY_DATABASE,
619
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DAVERAGE',
620
            'argumentCount' => '3',
621
        ],
622
        'DAY' => [
623
            'category' => Category::CATEGORY_DATE_AND_TIME,
624
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DAYOFMONTH',
625
            'argumentCount' => '1',
626
        ],
627
        'DAYS360' => [
628
            'category' => Category::CATEGORY_DATE_AND_TIME,
629
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DAYS360',
630
            'argumentCount' => '2,3',
631
        ],
632
        'DB' => [
633
            'category' => Category::CATEGORY_FINANCIAL,
634
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::DB',
635
            'argumentCount' => '4,5',
636
        ],
637
        'DCOUNT' => [
638
            'category' => Category::CATEGORY_DATABASE,
639
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DCOUNT',
640
            'argumentCount' => '3',
641
        ],
642
        'DCOUNTA' => [
643
            'category' => Category::CATEGORY_DATABASE,
644
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DCOUNTA',
645
            'argumentCount' => '3',
646
        ],
647
        'DDB' => [
648
            'category' => Category::CATEGORY_FINANCIAL,
649
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::DDB',
650
            'argumentCount' => '4,5',
651
        ],
652
        'DEC2BIN' => [
653
            'category' => Category::CATEGORY_ENGINEERING,
654
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::DECTOBIN',
655
            'argumentCount' => '1,2',
656
        ],
657
        'DEC2HEX' => [
658
            'category' => Category::CATEGORY_ENGINEERING,
659
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::DECTOHEX',
660
            'argumentCount' => '1,2',
661
        ],
662
        'DEC2OCT' => [
663
            'category' => Category::CATEGORY_ENGINEERING,
664
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::DECTOOCT',
665
            'argumentCount' => '1,2',
666
        ],
667
        'DEGREES' => [
668
            'category' => Category::CATEGORY_MATH_AND_TRIG,
669
            'functionCall' => 'rad2deg',
670
            'argumentCount' => '1',
671
        ],
672
        'DELTA' => [
673
            'category' => Category::CATEGORY_ENGINEERING,
674
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::DELTA',
675
            'argumentCount' => '1,2',
676
        ],
677
        'DEVSQ' => [
678
            'category' => Category::CATEGORY_STATISTICAL,
679
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::DEVSQ',
680
            'argumentCount' => '1+',
681
        ],
682
        'DGET' => [
683
            'category' => Category::CATEGORY_DATABASE,
684
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DGET',
685
            'argumentCount' => '3',
686
        ],
687
        'DISC' => [
688
            'category' => Category::CATEGORY_FINANCIAL,
689
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::DISC',
690
            'argumentCount' => '4,5',
691
        ],
692
        'DMAX' => [
693
            'category' => Category::CATEGORY_DATABASE,
694
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DMAX',
695
            'argumentCount' => '3',
696
        ],
697
        'DMIN' => [
698
            'category' => Category::CATEGORY_DATABASE,
699
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DMIN',
700
            'argumentCount' => '3',
701
        ],
702
        'DOLLAR' => [
703
            'category' => Category::CATEGORY_TEXT_AND_DATA,
704
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::DOLLAR',
705
            'argumentCount' => '1,2',
706
        ],
707
        'DOLLARDE' => [
708
            'category' => Category::CATEGORY_FINANCIAL,
709
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::DOLLARDE',
710
            'argumentCount' => '2',
711
        ],
712
        'DOLLARFR' => [
713
            'category' => Category::CATEGORY_FINANCIAL,
714
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::DOLLARFR',
715
            'argumentCount' => '2',
716
        ],
717
        'DPRODUCT' => [
718
            'category' => Category::CATEGORY_DATABASE,
719
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DPRODUCT',
720
            'argumentCount' => '3',
721
        ],
722
        'DSTDEV' => [
723
            'category' => Category::CATEGORY_DATABASE,
724
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DSTDEV',
725
            'argumentCount' => '3',
726
        ],
727
        'DSTDEVP' => [
728
            'category' => Category::CATEGORY_DATABASE,
729
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DSTDEVP',
730
            'argumentCount' => '3',
731
        ],
732
        'DSUM' => [
733
            'category' => Category::CATEGORY_DATABASE,
734
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DSUM',
735
            'argumentCount' => '3',
736
        ],
737
        'DURATION' => [
738
            'category' => Category::CATEGORY_FINANCIAL,
739
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
740
            'argumentCount' => '5,6',
741
        ],
742
        'DVAR' => [
743
            'category' => Category::CATEGORY_DATABASE,
744
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DVAR',
745
            'argumentCount' => '3',
746
        ],
747
        'DVARP' => [
748
            'category' => Category::CATEGORY_DATABASE,
749
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Database::DVARP',
750
            'argumentCount' => '3',
751
        ],
752
        'EDATE' => [
753
            'category' => Category::CATEGORY_DATE_AND_TIME,
754
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::EDATE',
755
            'argumentCount' => '2',
756
        ],
757
        'EFFECT' => [
758
            'category' => Category::CATEGORY_FINANCIAL,
759
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::EFFECT',
760
            'argumentCount' => '2',
761
        ],
762
        'EOMONTH' => [
763
            'category' => Category::CATEGORY_DATE_AND_TIME,
764
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::EOMONTH',
765
            'argumentCount' => '2',
766
        ],
767
        'ERF' => [
768
            'category' => Category::CATEGORY_ENGINEERING,
769
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::ERF',
770
            'argumentCount' => '1,2',
771
        ],
772
        'ERFC' => [
773
            'category' => Category::CATEGORY_ENGINEERING,
774
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::ERFC',
775
            'argumentCount' => '1',
776
        ],
777
        'ERROR.TYPE' => [
778
            'category' => Category::CATEGORY_INFORMATION,
779
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::errorType',
780
            'argumentCount' => '1',
781
        ],
782
        'EVEN' => [
783
            'category' => Category::CATEGORY_MATH_AND_TRIG,
784
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::EVEN',
785
            'argumentCount' => '1',
786
        ],
787
        'EXACT' => [
788
            'category' => Category::CATEGORY_TEXT_AND_DATA,
789
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
790
            'argumentCount' => '2',
791
        ],
792
        'EXP' => [
793
            'category' => Category::CATEGORY_MATH_AND_TRIG,
794
            'functionCall' => 'exp',
795
            'argumentCount' => '1',
796
        ],
797
        'EXPONDIST' => [
798
            'category' => Category::CATEGORY_STATISTICAL,
799
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::EXPONDIST',
800
            'argumentCount' => '3',
801
        ],
802
        'FACT' => [
803
            'category' => Category::CATEGORY_MATH_AND_TRIG,
804
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::FACT',
805
            'argumentCount' => '1',
806
        ],
807
        'FACTDOUBLE' => [
808
            'category' => Category::CATEGORY_MATH_AND_TRIG,
809
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::FACTDOUBLE',
810
            'argumentCount' => '1',
811
        ],
812
        'FALSE' => [
813
            'category' => Category::CATEGORY_LOGICAL,
814
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::FALSE',
815
            'argumentCount' => '0',
816
        ],
817
        'FDIST' => [
818
            'category' => Category::CATEGORY_STATISTICAL,
819
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
820
            'argumentCount' => '3',
821
        ],
822
        'FIND' => [
823
            'category' => Category::CATEGORY_TEXT_AND_DATA,
824
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::SEARCHSENSITIVE',
825
            'argumentCount' => '2,3',
826
        ],
827
        'FINDB' => [
828
            'category' => Category::CATEGORY_TEXT_AND_DATA,
829
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::SEARCHSENSITIVE',
830
            'argumentCount' => '2,3',
831
        ],
832
        'FINV' => [
833
            'category' => Category::CATEGORY_STATISTICAL,
834
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
835
            'argumentCount' => '3',
836
        ],
837
        'FISHER' => [
838
            'category' => Category::CATEGORY_STATISTICAL,
839
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::FISHER',
840
            'argumentCount' => '1',
841
        ],
842
        'FISHERINV' => [
843
            'category' => Category::CATEGORY_STATISTICAL,
844
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::FISHERINV',
845
            'argumentCount' => '1',
846
        ],
847
        'FIXED' => [
848
            'category' => Category::CATEGORY_TEXT_AND_DATA,
849
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::FIXEDFORMAT',
850
            'argumentCount' => '1-3',
851
        ],
852
        'FLOOR' => [
853
            'category' => Category::CATEGORY_MATH_AND_TRIG,
854
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::FLOOR',
855
            'argumentCount' => '2',
856
        ],
857
        'FORECAST' => [
858
            'category' => Category::CATEGORY_STATISTICAL,
859
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::FORECAST',
860
            'argumentCount' => '3',
861
        ],
862
        'FREQUENCY' => [
863
            'category' => Category::CATEGORY_STATISTICAL,
864
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
865
            'argumentCount' => '2',
866
        ],
867
        'FTEST' => [
868
            'category' => Category::CATEGORY_STATISTICAL,
869
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
870
            'argumentCount' => '2',
871
        ],
872
        'FV' => [
873
            'category' => Category::CATEGORY_FINANCIAL,
874
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::FV',
875
            'argumentCount' => '3-5',
876
        ],
877
        'FVSCHEDULE' => [
878
            'category' => Category::CATEGORY_FINANCIAL,
879
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::FVSCHEDULE',
880
            'argumentCount' => '2',
881
        ],
882
        'GAMMADIST' => [
883
            'category' => Category::CATEGORY_STATISTICAL,
884
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::GAMMADIST',
885
            'argumentCount' => '4',
886
        ],
887
        'GAMMAINV' => [
888
            'category' => Category::CATEGORY_STATISTICAL,
889
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::GAMMAINV',
890
            'argumentCount' => '3',
891
        ],
892
        'GAMMALN' => [
893
            'category' => Category::CATEGORY_STATISTICAL,
894
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::GAMMALN',
895
            'argumentCount' => '1',
896
        ],
897
        'GCD' => [
898
            'category' => Category::CATEGORY_MATH_AND_TRIG,
899
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::GCD',
900
            'argumentCount' => '1+',
901
        ],
902
        'GEOMEAN' => [
903
            'category' => Category::CATEGORY_STATISTICAL,
904
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::GEOMEAN',
905
            'argumentCount' => '1+',
906
        ],
907
        'GESTEP' => [
908
            'category' => Category::CATEGORY_ENGINEERING,
909
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::GESTEP',
910
            'argumentCount' => '1,2',
911
        ],
912
        'GETPIVOTDATA' => [
913
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
914
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
915
            'argumentCount' => '2+',
916
        ],
917
        'GROWTH' => [
918
            'category' => Category::CATEGORY_STATISTICAL,
919
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::GROWTH',
920
            'argumentCount' => '1-4',
921
        ],
922
        'HARMEAN' => [
923
            'category' => Category::CATEGORY_STATISTICAL,
924
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::HARMEAN',
925
            'argumentCount' => '1+',
926
        ],
927
        'HEX2BIN' => [
928
            'category' => Category::CATEGORY_ENGINEERING,
929
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::HEXTOBIN',
930
            'argumentCount' => '1,2',
931
        ],
932
        'HEX2DEC' => [
933
            'category' => Category::CATEGORY_ENGINEERING,
934
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::HEXTODEC',
935
            'argumentCount' => '1',
936
        ],
937
        'HEX2OCT' => [
938
            'category' => Category::CATEGORY_ENGINEERING,
939
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::HEXTOOCT',
940
            'argumentCount' => '1,2',
941
        ],
942
        'HLOOKUP' => [
943
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
944
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::HLOOKUP',
945
            'argumentCount' => '3,4',
946
        ],
947
        'HOUR' => [
948
            'category' => Category::CATEGORY_DATE_AND_TIME,
949
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::HOUROFDAY',
950
            'argumentCount' => '1',
951
        ],
952
        'HYPERLINK' => [
953
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
954
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::HYPERLINK',
955
            'argumentCount' => '1,2',
956
            'passCellReference' => true,
957
        ],
958
        'HYPGEOMDIST' => [
959
            'category' => Category::CATEGORY_STATISTICAL,
960
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::HYPGEOMDIST',
961
            'argumentCount' => '4',
962
        ],
963
        'IF' => [
964
            'category' => Category::CATEGORY_LOGICAL,
965
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::statementIf',
966
            'argumentCount' => '1-3',
967
        ],
968
        'IFERROR' => [
969
            'category' => Category::CATEGORY_LOGICAL,
970
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::IFERROR',
971
            'argumentCount' => '2',
972
        ],
973
        'IMABS' => [
974
            'category' => Category::CATEGORY_ENGINEERING,
975
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMABS',
976
            'argumentCount' => '1',
977
        ],
978
        'IMAGINARY' => [
979
            'category' => Category::CATEGORY_ENGINEERING,
980
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMAGINARY',
981
            'argumentCount' => '1',
982
        ],
983
        'IMARGUMENT' => [
984
            'category' => Category::CATEGORY_ENGINEERING,
985
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMARGUMENT',
986
            'argumentCount' => '1',
987
        ],
988
        'IMCONJUGATE' => [
989
            'category' => Category::CATEGORY_ENGINEERING,
990
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMCONJUGATE',
991
            'argumentCount' => '1',
992
        ],
993
        'IMCOS' => [
994
            'category' => Category::CATEGORY_ENGINEERING,
995
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMCOS',
996
            'argumentCount' => '1',
997
        ],
998
        'IMDIV' => [
999
            'category' => Category::CATEGORY_ENGINEERING,
1000
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMDIV',
1001
            'argumentCount' => '2',
1002
        ],
1003
        'IMEXP' => [
1004
            'category' => Category::CATEGORY_ENGINEERING,
1005
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMEXP',
1006
            'argumentCount' => '1',
1007
        ],
1008
        'IMLN' => [
1009
            'category' => Category::CATEGORY_ENGINEERING,
1010
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMLN',
1011
            'argumentCount' => '1',
1012
        ],
1013
        'IMLOG10' => [
1014
            'category' => Category::CATEGORY_ENGINEERING,
1015
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMLOG10',
1016
            'argumentCount' => '1',
1017
        ],
1018
        'IMLOG2' => [
1019
            'category' => Category::CATEGORY_ENGINEERING,
1020
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMLOG2',
1021
            'argumentCount' => '1',
1022
        ],
1023
        'IMPOWER' => [
1024
            'category' => Category::CATEGORY_ENGINEERING,
1025
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMPOWER',
1026
            'argumentCount' => '2',
1027
        ],
1028
        'IMPRODUCT' => [
1029
            'category' => Category::CATEGORY_ENGINEERING,
1030
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMPRODUCT',
1031
            'argumentCount' => '1+',
1032
        ],
1033
        'IMREAL' => [
1034
            'category' => Category::CATEGORY_ENGINEERING,
1035
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMREAL',
1036
            'argumentCount' => '1',
1037
        ],
1038
        'IMSIN' => [
1039
            'category' => Category::CATEGORY_ENGINEERING,
1040
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMSIN',
1041
            'argumentCount' => '1',
1042
        ],
1043
        'IMSQRT' => [
1044
            'category' => Category::CATEGORY_ENGINEERING,
1045
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMSQRT',
1046
            'argumentCount' => '1',
1047
        ],
1048
        'IMSUB' => [
1049
            'category' => Category::CATEGORY_ENGINEERING,
1050
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMSUB',
1051
            'argumentCount' => '2',
1052
        ],
1053
        'IMSUM' => [
1054
            'category' => Category::CATEGORY_ENGINEERING,
1055
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::IMSUM',
1056
            'argumentCount' => '1+',
1057
        ],
1058
        'INDEX' => [
1059
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1060
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::INDEX',
1061
            'argumentCount' => '1-4',
1062
        ],
1063
        'INDIRECT' => [
1064
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1065
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::INDIRECT',
1066
            'argumentCount' => '1,2',
1067
            'passCellReference' => true,
1068
        ],
1069
        'INFO' => [
1070
            'category' => Category::CATEGORY_INFORMATION,
1071
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1072
            'argumentCount' => '1',
1073
        ],
1074
        'INT' => [
1075
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1076
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::INT',
1077
            'argumentCount' => '1',
1078
        ],
1079
        'INTERCEPT' => [
1080
            'category' => Category::CATEGORY_STATISTICAL,
1081
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::INTERCEPT',
1082
            'argumentCount' => '2',
1083
        ],
1084
        'INTRATE' => [
1085
            'category' => Category::CATEGORY_FINANCIAL,
1086
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::INTRATE',
1087
            'argumentCount' => '4,5',
1088
        ],
1089
        'IPMT' => [
1090
            'category' => Category::CATEGORY_FINANCIAL,
1091
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::IPMT',
1092
            'argumentCount' => '4-6',
1093
        ],
1094
        'IRR' => [
1095
            'category' => Category::CATEGORY_FINANCIAL,
1096
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::IRR',
1097
            'argumentCount' => '1,2',
1098
        ],
1099
        'ISBLANK' => [
1100
            'category' => Category::CATEGORY_INFORMATION,
1101
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isBlank',
1102
            'argumentCount' => '1',
1103
        ],
1104
        'ISERR' => [
1105
            'category' => Category::CATEGORY_INFORMATION,
1106
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isErr',
1107
            'argumentCount' => '1',
1108
        ],
1109
        'ISERROR' => [
1110
            'category' => Category::CATEGORY_INFORMATION,
1111
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isError',
1112
            'argumentCount' => '1',
1113
        ],
1114
        'ISEVEN' => [
1115
            'category' => Category::CATEGORY_INFORMATION,
1116
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isEven',
1117
            'argumentCount' => '1',
1118
        ],
1119
        'ISLOGICAL' => [
1120
            'category' => Category::CATEGORY_INFORMATION,
1121
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isLogical',
1122
            'argumentCount' => '1',
1123
        ],
1124
        'ISNA' => [
1125
            'category' => Category::CATEGORY_INFORMATION,
1126
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isNa',
1127
            'argumentCount' => '1',
1128
        ],
1129
        'ISNONTEXT' => [
1130
            'category' => Category::CATEGORY_INFORMATION,
1131
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isNonText',
1132
            'argumentCount' => '1',
1133
        ],
1134
        'ISNUMBER' => [
1135
            'category' => Category::CATEGORY_INFORMATION,
1136
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isNumber',
1137
            'argumentCount' => '1',
1138
        ],
1139
        'ISODD' => [
1140
            'category' => Category::CATEGORY_INFORMATION,
1141
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isOdd',
1142
            'argumentCount' => '1',
1143
        ],
1144
        'ISPMT' => [
1145
            'category' => Category::CATEGORY_FINANCIAL,
1146
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::ISPMT',
1147
            'argumentCount' => '4',
1148
        ],
1149
        'ISREF' => [
1150
            'category' => Category::CATEGORY_INFORMATION,
1151
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1152
            'argumentCount' => '1',
1153
        ],
1154
        'ISTEXT' => [
1155
            'category' => Category::CATEGORY_INFORMATION,
1156
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::isText',
1157
            'argumentCount' => '1',
1158
        ],
1159
        'JIS' => [
1160
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1161
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1162
            'argumentCount' => '1',
1163
        ],
1164
        'KURT' => [
1165
            'category' => Category::CATEGORY_STATISTICAL,
1166
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::KURT',
1167
            'argumentCount' => '1+',
1168
        ],
1169
        'LARGE' => [
1170
            'category' => Category::CATEGORY_STATISTICAL,
1171
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::LARGE',
1172
            'argumentCount' => '2',
1173
        ],
1174
        'LCM' => [
1175
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1176
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::LCM',
1177
            'argumentCount' => '1+',
1178
        ],
1179
        'LEFT' => [
1180
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1181
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::LEFT',
1182
            'argumentCount' => '1,2',
1183
        ],
1184
        'LEFTB' => [
1185
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1186
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::LEFT',
1187
            'argumentCount' => '1,2',
1188
        ],
1189
        'LEN' => [
1190
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1191
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::STRINGLENGTH',
1192
            'argumentCount' => '1',
1193
        ],
1194
        'LENB' => [
1195
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1196
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::STRINGLENGTH',
1197
            'argumentCount' => '1',
1198
        ],
1199
        'LINEST' => [
1200
            'category' => Category::CATEGORY_STATISTICAL,
1201
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::LINEST',
1202
            'argumentCount' => '1-4',
1203
        ],
1204
        'LN' => [
1205
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1206
            'functionCall' => 'log',
1207
            'argumentCount' => '1',
1208
        ],
1209
        'LOG' => [
1210
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1211
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::logBase',
1212
            'argumentCount' => '1,2',
1213
        ],
1214
        'LOG10' => [
1215
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1216
            'functionCall' => 'log10',
1217
            'argumentCount' => '1',
1218
        ],
1219
        'LOGEST' => [
1220
            'category' => Category::CATEGORY_STATISTICAL,
1221
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::LOGEST',
1222
            'argumentCount' => '1-4',
1223
        ],
1224
        'LOGINV' => [
1225
            'category' => Category::CATEGORY_STATISTICAL,
1226
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::LOGINV',
1227
            'argumentCount' => '3',
1228
        ],
1229
        'LOGNORMDIST' => [
1230
            'category' => Category::CATEGORY_STATISTICAL,
1231
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::LOGNORMDIST',
1232
            'argumentCount' => '3',
1233
        ],
1234
        'LOOKUP' => [
1235
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1236
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::LOOKUP',
1237
            'argumentCount' => '2,3',
1238
        ],
1239
        'LOWER' => [
1240
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1241
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::LOWERCASE',
1242
            'argumentCount' => '1',
1243
        ],
1244
        'MATCH' => [
1245
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1246
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::MATCH',
1247
            'argumentCount' => '2,3',
1248
        ],
1249
        'MAX' => [
1250
            'category' => Category::CATEGORY_STATISTICAL,
1251
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MAX',
1252
            'argumentCount' => '1+',
1253
        ],
1254
        'MAXA' => [
1255
            'category' => Category::CATEGORY_STATISTICAL,
1256
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MAXA',
1257
            'argumentCount' => '1+',
1258
        ],
1259
        'MAXIF' => [
1260
            'category' => Category::CATEGORY_STATISTICAL,
1261
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MAXIF',
1262
            'argumentCount' => '2+',
1263
        ],
1264
        'MDETERM' => [
1265
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1266
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::MDETERM',
1267
            'argumentCount' => '1',
1268
        ],
1269
        'MDURATION' => [
1270
            'category' => Category::CATEGORY_FINANCIAL,
1271
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1272
            'argumentCount' => '5,6',
1273
        ],
1274
        'MEDIAN' => [
1275
            'category' => Category::CATEGORY_STATISTICAL,
1276
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MEDIAN',
1277
            'argumentCount' => '1+',
1278
        ],
1279
        'MEDIANIF' => [
1280
            'category' => Category::CATEGORY_STATISTICAL,
1281
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1282
            'argumentCount' => '2+',
1283
        ],
1284
        'MID' => [
1285
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1286
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::MID',
1287
            'argumentCount' => '3',
1288
        ],
1289
        'MIDB' => [
1290
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1291
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::MID',
1292
            'argumentCount' => '3',
1293
        ],
1294
        'MIN' => [
1295
            'category' => Category::CATEGORY_STATISTICAL,
1296
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MIN',
1297
            'argumentCount' => '1+',
1298
        ],
1299
        'MINA' => [
1300
            'category' => Category::CATEGORY_STATISTICAL,
1301
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MINA',
1302
            'argumentCount' => '1+',
1303
        ],
1304
        'MINIF' => [
1305
            'category' => Category::CATEGORY_STATISTICAL,
1306
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MINIF',
1307
            'argumentCount' => '2+',
1308
        ],
1309
        'MINUTE' => [
1310
            'category' => Category::CATEGORY_DATE_AND_TIME,
1311
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::MINUTE',
1312
            'argumentCount' => '1',
1313
        ],
1314
        'MINVERSE' => [
1315
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1316
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::MINVERSE',
1317
            'argumentCount' => '1',
1318
        ],
1319
        'MIRR' => [
1320
            'category' => Category::CATEGORY_FINANCIAL,
1321
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::MIRR',
1322
            'argumentCount' => '3',
1323
        ],
1324
        'MMULT' => [
1325
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1326
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::MMULT',
1327
            'argumentCount' => '2',
1328
        ],
1329
        'MOD' => [
1330
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1331
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::MOD',
1332
            'argumentCount' => '2',
1333
        ],
1334
        'MODE' => [
1335
            'category' => Category::CATEGORY_STATISTICAL,
1336
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::MODE',
1337
            'argumentCount' => '1+',
1338
        ],
1339
        'MONTH' => [
1340
            'category' => Category::CATEGORY_DATE_AND_TIME,
1341
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::MONTHOFYEAR',
1342
            'argumentCount' => '1',
1343
        ],
1344
        'MROUND' => [
1345
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1346
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::MROUND',
1347
            'argumentCount' => '2',
1348
        ],
1349
        'MULTINOMIAL' => [
1350
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1351
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::MULTINOMIAL',
1352
            'argumentCount' => '1+',
1353
        ],
1354
        'N' => [
1355
            'category' => Category::CATEGORY_INFORMATION,
1356
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::n',
1357
            'argumentCount' => '1',
1358
        ],
1359
        'NA' => [
1360
            'category' => Category::CATEGORY_INFORMATION,
1361
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::NA',
1362
            'argumentCount' => '0',
1363
        ],
1364
        'NEGBINOMDIST' => [
1365
            'category' => Category::CATEGORY_STATISTICAL,
1366
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::NEGBINOMDIST',
1367
            'argumentCount' => '3',
1368
        ],
1369
        'NETWORKDAYS' => [
1370
            'category' => Category::CATEGORY_DATE_AND_TIME,
1371
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::NETWORKDAYS',
1372
            'argumentCount' => '2+',
1373
        ],
1374
        'NOMINAL' => [
1375
            'category' => Category::CATEGORY_FINANCIAL,
1376
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::NOMINAL',
1377
            'argumentCount' => '2',
1378
        ],
1379
        'NORMDIST' => [
1380
            'category' => Category::CATEGORY_STATISTICAL,
1381
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::NORMDIST',
1382
            'argumentCount' => '4',
1383
        ],
1384
        'NORMINV' => [
1385
            'category' => Category::CATEGORY_STATISTICAL,
1386
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::NORMINV',
1387
            'argumentCount' => '3',
1388
        ],
1389
        'NORMSDIST' => [
1390
            'category' => Category::CATEGORY_STATISTICAL,
1391
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::NORMSDIST',
1392
            'argumentCount' => '1',
1393
        ],
1394
        'NORMSINV' => [
1395
            'category' => Category::CATEGORY_STATISTICAL,
1396
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::NORMSINV',
1397
            'argumentCount' => '1',
1398
        ],
1399
        'NOT' => [
1400
            'category' => Category::CATEGORY_LOGICAL,
1401
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::NOT',
1402
            'argumentCount' => '1',
1403
        ],
1404
        'NOW' => [
1405
            'category' => Category::CATEGORY_DATE_AND_TIME,
1406
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DATETIMENOW',
1407
            'argumentCount' => '0',
1408
        ],
1409
        'NPER' => [
1410
            'category' => Category::CATEGORY_FINANCIAL,
1411
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::NPER',
1412
            'argumentCount' => '3-5',
1413
        ],
1414
        'NPV' => [
1415
            'category' => Category::CATEGORY_FINANCIAL,
1416
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::NPV',
1417
            'argumentCount' => '2+',
1418
        ],
1419
        'OCT2BIN' => [
1420
            'category' => Category::CATEGORY_ENGINEERING,
1421
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::OCTTOBIN',
1422
            'argumentCount' => '1,2',
1423
        ],
1424
        'OCT2DEC' => [
1425
            'category' => Category::CATEGORY_ENGINEERING,
1426
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::OCTTODEC',
1427
            'argumentCount' => '1',
1428
        ],
1429
        'OCT2HEX' => [
1430
            'category' => Category::CATEGORY_ENGINEERING,
1431
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Engineering::OCTTOHEX',
1432
            'argumentCount' => '1,2',
1433
        ],
1434
        'ODD' => [
1435
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1436
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::ODD',
1437
            'argumentCount' => '1',
1438
        ],
1439
        'ODDFPRICE' => [
1440
            'category' => Category::CATEGORY_FINANCIAL,
1441
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1442
            'argumentCount' => '8,9',
1443
        ],
1444
        'ODDFYIELD' => [
1445
            'category' => Category::CATEGORY_FINANCIAL,
1446
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1447
            'argumentCount' => '8,9',
1448
        ],
1449
        'ODDLPRICE' => [
1450
            'category' => Category::CATEGORY_FINANCIAL,
1451
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1452
            'argumentCount' => '7,8',
1453
        ],
1454
        'ODDLYIELD' => [
1455
            'category' => Category::CATEGORY_FINANCIAL,
1456
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1457
            'argumentCount' => '7,8',
1458
        ],
1459
        'OFFSET' => [
1460
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1461
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::OFFSET',
1462
            'argumentCount' => '3-5',
1463
            'passCellReference' => true,
1464
            'passByReference' => [true],
1465
        ],
1466
        'OR' => [
1467
            'category' => Category::CATEGORY_LOGICAL,
1468
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::logicalOr',
1469
            'argumentCount' => '1+',
1470
        ],
1471
        'PEARSON' => [
1472
            'category' => Category::CATEGORY_STATISTICAL,
1473
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::CORREL',
1474
            'argumentCount' => '2',
1475
        ],
1476
        'PERCENTILE' => [
1477
            'category' => Category::CATEGORY_STATISTICAL,
1478
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::PERCENTILE',
1479
            'argumentCount' => '2',
1480
        ],
1481
        'PERCENTRANK' => [
1482
            'category' => Category::CATEGORY_STATISTICAL,
1483
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::PERCENTRANK',
1484
            'argumentCount' => '2,3',
1485
        ],
1486
        'PERMUT' => [
1487
            'category' => Category::CATEGORY_STATISTICAL,
1488
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::PERMUT',
1489
            'argumentCount' => '2',
1490
        ],
1491
        'PHONETIC' => [
1492
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1493
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1494
            'argumentCount' => '1',
1495
        ],
1496
        'PI' => [
1497
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1498
            'functionCall' => 'pi',
1499
            'argumentCount' => '0',
1500
        ],
1501
        'PMT' => [
1502
            'category' => Category::CATEGORY_FINANCIAL,
1503
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::PMT',
1504
            'argumentCount' => '3-5',
1505
        ],
1506
        'POISSON' => [
1507
            'category' => Category::CATEGORY_STATISTICAL,
1508
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::POISSON',
1509
            'argumentCount' => '3',
1510
        ],
1511
        'POWER' => [
1512
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1513
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::POWER',
1514
            'argumentCount' => '2',
1515
        ],
1516
        'PPMT' => [
1517
            'category' => Category::CATEGORY_FINANCIAL,
1518
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::PPMT',
1519
            'argumentCount' => '4-6',
1520
        ],
1521
        'PRICE' => [
1522
            'category' => Category::CATEGORY_FINANCIAL,
1523
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::PRICE',
1524
            'argumentCount' => '6,7',
1525
        ],
1526
        'PRICEDISC' => [
1527
            'category' => Category::CATEGORY_FINANCIAL,
1528
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::PRICEDISC',
1529
            'argumentCount' => '4,5',
1530
        ],
1531
        'PRICEMAT' => [
1532
            'category' => Category::CATEGORY_FINANCIAL,
1533
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::PRICEMAT',
1534
            'argumentCount' => '5,6',
1535
        ],
1536
        'PROB' => [
1537
            'category' => Category::CATEGORY_STATISTICAL,
1538
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1539
            'argumentCount' => '3,4',
1540
        ],
1541
        'PRODUCT' => [
1542
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1543
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::PRODUCT',
1544
            'argumentCount' => '1+',
1545
        ],
1546
        'PROPER' => [
1547
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1548
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::PROPERCASE',
1549
            'argumentCount' => '1',
1550
        ],
1551
        'PV' => [
1552
            'category' => Category::CATEGORY_FINANCIAL,
1553
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::PV',
1554
            'argumentCount' => '3-5',
1555
        ],
1556
        'QUARTILE' => [
1557
            'category' => Category::CATEGORY_STATISTICAL,
1558
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::QUARTILE',
1559
            'argumentCount' => '2',
1560
        ],
1561
        'QUOTIENT' => [
1562
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1563
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::QUOTIENT',
1564
            'argumentCount' => '2',
1565
        ],
1566
        'RADIANS' => [
1567
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1568
            'functionCall' => 'deg2rad',
1569
            'argumentCount' => '1',
1570
        ],
1571
        'RAND' => [
1572
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1573
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::RAND',
1574
            'argumentCount' => '0',
1575
        ],
1576
        'RANDBETWEEN' => [
1577
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1578
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::RAND',
1579
            'argumentCount' => '2',
1580
        ],
1581
        'RANK' => [
1582
            'category' => Category::CATEGORY_STATISTICAL,
1583
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::RANK',
1584
            'argumentCount' => '2,3',
1585
        ],
1586
        'RATE' => [
1587
            'category' => Category::CATEGORY_FINANCIAL,
1588
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::RATE',
1589
            'argumentCount' => '3-6',
1590
        ],
1591
        'RECEIVED' => [
1592
            'category' => Category::CATEGORY_FINANCIAL,
1593
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::RECEIVED',
1594
            'argumentCount' => '4-5',
1595
        ],
1596
        'REPLACE' => [
1597
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1598
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::REPLACE',
1599
            'argumentCount' => '4',
1600
        ],
1601
        'REPLACEB' => [
1602
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1603
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::REPLACE',
1604
            'argumentCount' => '4',
1605
        ],
1606
        'REPT' => [
1607
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1608
            'functionCall' => 'str_repeat',
1609
            'argumentCount' => '2',
1610
        ],
1611
        'RIGHT' => [
1612
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1613
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::RIGHT',
1614
            'argumentCount' => '1,2',
1615
        ],
1616
        'RIGHTB' => [
1617
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1618
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::RIGHT',
1619
            'argumentCount' => '1,2',
1620
        ],
1621
        'ROMAN' => [
1622
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1623
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::ROMAN',
1624
            'argumentCount' => '1,2',
1625
        ],
1626
        'ROUND' => [
1627
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1628
            'functionCall' => 'round',
1629
            'argumentCount' => '2',
1630
        ],
1631
        'ROUNDDOWN' => [
1632
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1633
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::ROUNDDOWN',
1634
            'argumentCount' => '2',
1635
        ],
1636
        'ROUNDUP' => [
1637
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1638
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::ROUNDUP',
1639
            'argumentCount' => '2',
1640
        ],
1641
        'ROW' => [
1642
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1643
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::ROW',
1644
            'argumentCount' => '-1',
1645
            'passByReference' => [true],
1646
        ],
1647
        'ROWS' => [
1648
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1649
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::ROWS',
1650
            'argumentCount' => '1',
1651
        ],
1652
        'RSQ' => [
1653
            'category' => Category::CATEGORY_STATISTICAL,
1654
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::RSQ',
1655
            'argumentCount' => '2',
1656
        ],
1657
        'RTD' => [
1658
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1659
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1660
            'argumentCount' => '1+',
1661
        ],
1662
        'SEARCH' => [
1663
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1664
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::SEARCHINSENSITIVE',
1665
            'argumentCount' => '2,3',
1666
        ],
1667
        'SEARCHB' => [
1668
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1669
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::SEARCHINSENSITIVE',
1670
            'argumentCount' => '2,3',
1671
        ],
1672
        'SECOND' => [
1673
            'category' => Category::CATEGORY_DATE_AND_TIME,
1674
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::SECOND',
1675
            'argumentCount' => '1',
1676
        ],
1677
        'SERIESSUM' => [
1678
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1679
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SERIESSUM',
1680
            'argumentCount' => '4',
1681
        ],
1682
        'SIGN' => [
1683
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1684
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SIGN',
1685
            'argumentCount' => '1',
1686
        ],
1687
        'SIN' => [
1688
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1689
            'functionCall' => 'sin',
1690
            'argumentCount' => '1',
1691
        ],
1692
        'SINH' => [
1693
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1694
            'functionCall' => 'sinh',
1695
            'argumentCount' => '1',
1696
        ],
1697
        'SKEW' => [
1698
            'category' => Category::CATEGORY_STATISTICAL,
1699
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::SKEW',
1700
            'argumentCount' => '1+',
1701
        ],
1702
        'SLN' => [
1703
            'category' => Category::CATEGORY_FINANCIAL,
1704
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::SLN',
1705
            'argumentCount' => '3',
1706
        ],
1707
        'SLOPE' => [
1708
            'category' => Category::CATEGORY_STATISTICAL,
1709
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::SLOPE',
1710
            'argumentCount' => '2',
1711
        ],
1712
        'SMALL' => [
1713
            'category' => Category::CATEGORY_STATISTICAL,
1714
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::SMALL',
1715
            'argumentCount' => '2',
1716
        ],
1717
        'SQRT' => [
1718
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1719
            'functionCall' => 'sqrt',
1720
            'argumentCount' => '1',
1721
        ],
1722
        'SQRTPI' => [
1723
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1724
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SQRTPI',
1725
            'argumentCount' => '1',
1726
        ],
1727
        'STANDARDIZE' => [
1728
            'category' => Category::CATEGORY_STATISTICAL,
1729
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::STANDARDIZE',
1730
            'argumentCount' => '3',
1731
        ],
1732
        'STDEV' => [
1733
            'category' => Category::CATEGORY_STATISTICAL,
1734
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::STDEV',
1735
            'argumentCount' => '1+',
1736
        ],
1737
        'STDEVA' => [
1738
            'category' => Category::CATEGORY_STATISTICAL,
1739
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::STDEVA',
1740
            'argumentCount' => '1+',
1741
        ],
1742
        'STDEVP' => [
1743
            'category' => Category::CATEGORY_STATISTICAL,
1744
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::STDEVP',
1745
            'argumentCount' => '1+',
1746
        ],
1747
        'STDEVPA' => [
1748
            'category' => Category::CATEGORY_STATISTICAL,
1749
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::STDEVPA',
1750
            'argumentCount' => '1+',
1751
        ],
1752
        'STEYX' => [
1753
            'category' => Category::CATEGORY_STATISTICAL,
1754
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::STEYX',
1755
            'argumentCount' => '2',
1756
        ],
1757
        'SUBSTITUTE' => [
1758
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1759
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::SUBSTITUTE',
1760
            'argumentCount' => '3,4',
1761
        ],
1762
        'SUBTOTAL' => [
1763
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1764
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUBTOTAL',
1765
            'argumentCount' => '2+',
1766
        ],
1767
        'SUM' => [
1768
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1769
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUM',
1770
            'argumentCount' => '1+',
1771
        ],
1772
        'SUMIF' => [
1773
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1774
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMIF',
1775
            'argumentCount' => '2,3',
1776
        ],
1777
        'SUMIFS' => [
1778
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1779
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMIFS',
1780
            'argumentCount' => '3+',
1781
        ],
1782
        'SUMPRODUCT' => [
1783
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1784
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMPRODUCT',
1785
            'argumentCount' => '1+',
1786
        ],
1787
        'SUMSQ' => [
1788
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1789
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMSQ',
1790
            'argumentCount' => '1+',
1791
        ],
1792
        'SUMX2MY2' => [
1793
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1794
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMX2MY2',
1795
            'argumentCount' => '2',
1796
        ],
1797
        'SUMX2PY2' => [
1798
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1799
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMX2PY2',
1800
            'argumentCount' => '2',
1801
        ],
1802
        'SUMXMY2' => [
1803
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1804
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::SUMXMY2',
1805
            'argumentCount' => '2',
1806
        ],
1807
        'SYD' => [
1808
            'category' => Category::CATEGORY_FINANCIAL,
1809
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::SYD',
1810
            'argumentCount' => '4',
1811
        ],
1812
        'T' => [
1813
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1814
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::RETURNSTRING',
1815
            'argumentCount' => '1',
1816
        ],
1817
        'TAN' => [
1818
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1819
            'functionCall' => 'tan',
1820
            'argumentCount' => '1',
1821
        ],
1822
        'TANH' => [
1823
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1824
            'functionCall' => 'tanh',
1825
            'argumentCount' => '1',
1826
        ],
1827
        'TBILLEQ' => [
1828
            'category' => Category::CATEGORY_FINANCIAL,
1829
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::TBILLEQ',
1830
            'argumentCount' => '3',
1831
        ],
1832
        'TBILLPRICE' => [
1833
            'category' => Category::CATEGORY_FINANCIAL,
1834
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::TBILLPRICE',
1835
            'argumentCount' => '3',
1836
        ],
1837
        'TBILLYIELD' => [
1838
            'category' => Category::CATEGORY_FINANCIAL,
1839
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::TBILLYIELD',
1840
            'argumentCount' => '3',
1841
        ],
1842
        'TDIST' => [
1843
            'category' => Category::CATEGORY_STATISTICAL,
1844
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::TDIST',
1845
            'argumentCount' => '3',
1846
        ],
1847
        'TEXT' => [
1848
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1849
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::TEXTFORMAT',
1850
            'argumentCount' => '2',
1851
        ],
1852
        'TIME' => [
1853
            'category' => Category::CATEGORY_DATE_AND_TIME,
1854
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::TIME',
1855
            'argumentCount' => '3',
1856
        ],
1857
        'TIMEVALUE' => [
1858
            'category' => Category::CATEGORY_DATE_AND_TIME,
1859
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::TIMEVALUE',
1860
            'argumentCount' => '1',
1861
        ],
1862
        'TINV' => [
1863
            'category' => Category::CATEGORY_STATISTICAL,
1864
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::TINV',
1865
            'argumentCount' => '2',
1866
        ],
1867
        'TODAY' => [
1868
            'category' => Category::CATEGORY_DATE_AND_TIME,
1869
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::DATENOW',
1870
            'argumentCount' => '0',
1871
        ],
1872
        'TRANSPOSE' => [
1873
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1874
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::TRANSPOSE',
1875
            'argumentCount' => '1',
1876
        ],
1877
        'TREND' => [
1878
            'category' => Category::CATEGORY_STATISTICAL,
1879
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::TREND',
1880
            'argumentCount' => '1-4',
1881
        ],
1882
        'TRIM' => [
1883
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1884
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::TRIMSPACES',
1885
            'argumentCount' => '1',
1886
        ],
1887
        'TRIMMEAN' => [
1888
            'category' => Category::CATEGORY_STATISTICAL,
1889
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::TRIMMEAN',
1890
            'argumentCount' => '2',
1891
        ],
1892
        'TRUE' => [
1893
            'category' => Category::CATEGORY_LOGICAL,
1894
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Logical::TRUE',
1895
            'argumentCount' => '0',
1896
        ],
1897
        'TRUNC' => [
1898
            'category' => Category::CATEGORY_MATH_AND_TRIG,
1899
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig::TRUNC',
1900
            'argumentCount' => '1,2',
1901
        ],
1902
        'TTEST' => [
1903
            'category' => Category::CATEGORY_STATISTICAL,
1904
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1905
            'argumentCount' => '4',
1906
        ],
1907
        'TYPE' => [
1908
            'category' => Category::CATEGORY_INFORMATION,
1909
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::TYPE',
1910
            'argumentCount' => '1',
1911
        ],
1912
        'UPPER' => [
1913
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1914
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::UPPERCASE',
1915
            'argumentCount' => '1',
1916
        ],
1917
        'USDOLLAR' => [
1918
            'category' => Category::CATEGORY_FINANCIAL,
1919
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1920
            'argumentCount' => '2',
1921
        ],
1922
        'VALUE' => [
1923
            'category' => Category::CATEGORY_TEXT_AND_DATA,
1924
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\TextData::VALUE',
1925
            'argumentCount' => '1',
1926
        ],
1927
        'VAR' => [
1928
            'category' => Category::CATEGORY_STATISTICAL,
1929
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::VARFunc',
1930
            'argumentCount' => '1+',
1931
        ],
1932
        'VARA' => [
1933
            'category' => Category::CATEGORY_STATISTICAL,
1934
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::VARA',
1935
            'argumentCount' => '1+',
1936
        ],
1937
        'VARP' => [
1938
            'category' => Category::CATEGORY_STATISTICAL,
1939
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::VARP',
1940
            'argumentCount' => '1+',
1941
        ],
1942
        'VARPA' => [
1943
            'category' => Category::CATEGORY_STATISTICAL,
1944
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::VARPA',
1945
            'argumentCount' => '1+',
1946
        ],
1947
        'VDB' => [
1948
            'category' => Category::CATEGORY_FINANCIAL,
1949
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
1950
            'argumentCount' => '5-7',
1951
        ],
1952
        'VLOOKUP' => [
1953
            'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1954
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef::VLOOKUP',
1955
            'argumentCount' => '3,4',
1956
        ],
1957
        'WEEKDAY' => [
1958
            'category' => Category::CATEGORY_DATE_AND_TIME,
1959
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::WEEKDAY',
1960
            'argumentCount' => '1,2',
1961
        ],
1962
        'WEEKNUM' => [
1963
            'category' => Category::CATEGORY_DATE_AND_TIME,
1964
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::WEEKNUM',
1965
            'argumentCount' => '1,2',
1966
        ],
1967
        'WEIBULL' => [
1968
            'category' => Category::CATEGORY_STATISTICAL,
1969
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::WEIBULL',
1970
            'argumentCount' => '4',
1971
        ],
1972
        'WORKDAY' => [
1973
            'category' => Category::CATEGORY_DATE_AND_TIME,
1974
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::WORKDAY',
1975
            'argumentCount' => '2+',
1976
        ],
1977
        'XIRR' => [
1978
            'category' => Category::CATEGORY_FINANCIAL,
1979
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::XIRR',
1980
            'argumentCount' => '2,3',
1981
        ],
1982
        'XNPV' => [
1983
            'category' => Category::CATEGORY_FINANCIAL,
1984
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::XNPV',
1985
            'argumentCount' => '3',
1986
        ],
1987
        'YEAR' => [
1988
            'category' => Category::CATEGORY_DATE_AND_TIME,
1989
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::YEAR',
1990
            'argumentCount' => '1',
1991
        ],
1992
        'YEARFRAC' => [
1993
            'category' => Category::CATEGORY_DATE_AND_TIME,
1994
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\DateTime::YEARFRAC',
1995
            'argumentCount' => '2,3',
1996
        ],
1997
        'YIELD' => [
1998
            'category' => Category::CATEGORY_FINANCIAL,
1999
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY',
2000
            'argumentCount' => '6,7',
2001
        ],
2002
        'YIELDDISC' => [
2003
            'category' => Category::CATEGORY_FINANCIAL,
2004
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::YIELDDISC',
2005
            'argumentCount' => '4,5',
2006
        ],
2007
        'YIELDMAT' => [
2008
            'category' => Category::CATEGORY_FINANCIAL,
2009
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Financial::YIELDMAT',
2010
            'argumentCount' => '5,6',
2011
        ],
2012
        'ZTEST' => [
2013
            'category' => Category::CATEGORY_STATISTICAL,
2014
            'functionCall' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical::ZTEST',
2015
            'argumentCount' => '2-3',
2016
        ],
2017
    ];
2018
2019
    //    Internal functions used for special control purposes
2020
    private static $controlFunctions = [
2021
        'MKMATRIX' => [
2022
            'argumentCount' => '*',
2023
            'functionCall' => 'self::mkMatrix',
2024
        ],
2025
    ];
2026
2027 62
    public function __construct(Spreadsheet $spreadsheet = null)
2028
    {
2029 62
        $this->delta = 1 * pow(10, 0 - ini_get('precision'));
0 ignored issues
show
Documentation Bug introduced by
It seems like 1 * pow(10, 0 - ini_get('precision')) can also be of type integer. However, the property $delta is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2030
2031 62
        $this->spreadsheet = $spreadsheet;
0 ignored issues
show
Documentation Bug introduced by
It seems like $spreadsheet can also be of type object<PhpOffice\PhpSpreadsheet\Spreadsheet>. However, the property $spreadsheet is declared as type object<PhpOffice\PhpSpreadsheet\PhpSpreadsheet>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2032 62
        $this->cyclicReferenceStack = new CalcEngine\CyclicReferenceStack();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \PhpOffice\PhpSpread...\CyclicReferenceStack() of type object<PhpOffice\PhpSpre...e\CyclicReferenceStack> is incompatible with the declared type array of property $cyclicReferenceStack.

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...
2033 62
        $this->_debugLog = new CalcEngine\Logger($this->cyclicReferenceStack);
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2034 62
    }
2035
2036
    private static function loadLocales()
2037
    {
2038
        $localeFileDirectory = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet/locale/';
2039
        foreach (glob($localeFileDirectory . '/*', GLOB_ONLYDIR) as $filename) {
2040
            $filename = substr($filename, strlen($localeFileDirectory) + 1);
2041
            if ($filename != 'en') {
2042
                self::$validLocaleLanguages[] = $filename;
2043
            }
2044
        }
2045
    }
2046
2047
    /**
2048
     * Get an instance of this class
2049
     *
2050
     * @param   Spreadsheet $spreadsheet  Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
2051
     *                                    or NULL to create a standalone claculation engine
2052
     * @return Calculation
2053
     */
2054 117
    public static function getInstance(Spreadsheet $spreadsheet = null)
2055
    {
2056 117
        if ($spreadsheet !== null) {
2057 60
            $instance = $spreadsheet->getCalculationEngine();
2058 60
            if (isset($instance)) {
2059 60
                return $instance;
2060
            }
2061
        }
2062
2063 60
        if (!isset(self::$instance) || (self::$instance === null)) {
2064 3
            self::$instance = new \PhpOffice\PhpSpreadsheet\Calculation();
2065
        }
2066
2067 60
        return self::$instance;
2068
    }
2069
2070
    /**
2071
     * Unset an instance of this class
2072
     *
2073
     * @param   Spreadsheet $spreadsheet  Injected spreadsheet identifying the instance to unset
0 ignored issues
show
Bug introduced by
There is no parameter named $spreadsheet. Was it maybe removed?

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

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

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

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

Loading history...
2074
     */
2075 1
    public function __destruct()
2076
    {
2077 1
        $this->workbook = null;
0 ignored issues
show
Bug introduced by
The property workbook 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...
2078 1
    }
2079
2080
    /**
2081
     * Flush the calculation cache for any existing instance of this class
2082
     *        but only if a \PhpOffice\PhpSpreadsheet\Calculation instance exists
2083
     */
2084
    public function flushInstance()
2085
    {
2086
        $this->clearCalculationCache();
2087
    }
2088
2089
    /**
2090
     * Get the debuglog for this claculation engine instance
2091
     *
2092
     * @return CalcEngine\Logger
2093
     */
2094 59
    public function getDebugLog()
2095
    {
2096 59
        return $this->_debugLog;
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2097
    }
2098
2099
    /**
2100
     * __clone implementation. Cloning should not be allowed in a Singleton!
2101
     *
2102
     * @throws    Calculation\Exception
2103
     */
2104
    final public function __clone()
2105
    {
2106
        throw new Calculation\Exception('Cloning the calculation engine is not allowed!');
2107
    }
2108
2109
    /**
2110
     * Return the locale-specific translation of TRUE
2111
     *
2112
     * @return     string        locale-specific translation of TRUE
2113
     */
2114 36
    public static function getTRUE()
2115
    {
2116 36
        return self::$localeBoolean['TRUE'];
2117
    }
2118
2119
    /**
2120
     * Return the locale-specific translation of FALSE
2121
     *
2122
     * @return     string        locale-specific translation of FALSE
2123
     */
2124 29
    public static function getFALSE()
2125
    {
2126 29
        return self::$localeBoolean['FALSE'];
2127
    }
2128
2129
    /**
2130
     * Set the Array Return Type (Array or Value of first element in the array)
2131
     *
2132
     * @param     string    $returnType            Array return type
2133
     * @return     bool                    Success or failure
2134
     */
2135 15
    public static function setArrayReturnType($returnType)
2136
    {
2137 15
        if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
2138 11
            ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
2139 15
            ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
2140 15
            self::$returnArrayAsType = $returnType;
2141
2142 15
            return true;
2143
        }
2144
2145
        return false;
2146
    }
2147
2148
    /**
2149
     * Return the Array Return Type (Array or Value of first element in the array)
2150
     *
2151
     * @return     string        $returnType            Array return type
2152
     */
2153 4
    public static function getArrayReturnType()
2154
    {
2155 4
        return self::$returnArrayAsType;
2156
    }
2157
2158
    /**
2159
     * Is calculation caching enabled?
2160
     *
2161
     * @return bool
2162
     */
2163
    public function getCalculationCacheEnabled()
2164
    {
2165
        return $this->calculationCacheEnabled;
2166
    }
2167
2168
    /**
2169
     * Enable/disable calculation cache
2170
     *
2171
     * @param bool $pValue
2172
     */
2173
    public function setCalculationCacheEnabled($pValue = true)
2174
    {
2175
        $this->calculationCacheEnabled = $pValue;
2176
        $this->clearCalculationCache();
2177
    }
2178
2179
    /**
2180
     * Enable calculation cache
2181
     */
2182
    public function enableCalculationCache()
2183
    {
2184
        $this->setCalculationCacheEnabled(true);
2185
    }
2186
2187
    /**
2188
     * Disable calculation cache
2189
     */
2190
    public function disableCalculationCache()
2191
    {
2192
        $this->setCalculationCacheEnabled(false);
2193
    }
2194
2195
    /**
2196
     * Clear calculation cache
2197
     */
2198
    public function clearCalculationCache()
2199
    {
2200
        $this->calculationCache = [];
2201
    }
2202
2203
    /**
2204
     * Clear calculation cache for a specified worksheet
2205
     *
2206
     * @param string $worksheetName
2207
     */
2208 1
    public function clearCalculationCacheForWorksheet($worksheetName)
2209
    {
2210 1
        if (isset($this->calculationCache[$worksheetName])) {
2211
            unset($this->calculationCache[$worksheetName]);
2212
        }
2213 1
    }
2214
2215
    /**
2216
     * Rename calculation cache for a specified worksheet
2217
     *
2218
     * @param string $fromWorksheetName
2219
     * @param string $toWorksheetName
2220
     */
2221 62
    public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName)
2222
    {
2223 62
        if (isset($this->calculationCache[$fromWorksheetName])) {
2224
            $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
2225
            unset($this->calculationCache[$fromWorksheetName]);
2226
        }
2227 62
    }
2228
2229
    /**
2230
     * Get the currently defined locale code
2231
     *
2232
     * @return string
2233
     */
2234
    public function getLocale()
2235
    {
2236
        return self::$localeLanguage;
2237
    }
2238
2239
    /**
2240
     * Set the locale code
2241
     *
2242
     * @param string $locale  The locale to use for formula translation
2243
     * @return bool
2244
     */
2245
    public function setLocale($locale = 'en_us')
2246
    {
2247
        //    Identify our locale and language
2248
        $language = $locale = strtolower($locale);
2249
        if (strpos($locale, '_') !== false) {
2250
            list($language) = explode('_', $locale);
2251
        }
2252
2253
        if (count(self::$validLocaleLanguages) == 1) {
2254
            self::loadLocales();
2255
        }
2256
        //    Test whether we have any language data for this language (any locale)
2257
        if (in_array($language, self::$validLocaleLanguages)) {
2258
            //    initialise language/locale settings
2259
            self::$localeFunctions = [];
2260
            self::$localeArgumentSeparator = ',';
2261
            self::$localeBoolean = ['TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL'];
0 ignored issues
show
Documentation Bug introduced by
It seems like array('TRUE' => 'TRUE', ...LSE', 'NULL' => 'NULL') of type array<string,string,{"TR...ring","NULL":"string"}> is incompatible with the declared type array<integer,string> of property $localeBoolean.

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...
2262
            //    Default is English, if user isn't requesting english, then read the necessary data from the locale files
2263
            if ($locale != 'en_us') {
2264
                //    Search for a file with a list of function names for locale
2265
                $functionNamesFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'functions';
2266
                if (!file_exists($functionNamesFile)) {
2267
                    //    If there isn't a locale specific function file, look for a language specific function file
2268
                    $functionNamesFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . 'functions';
2269
                    if (!file_exists($functionNamesFile)) {
2270
                        return false;
2271
                    }
2272
                }
2273
                //    Retrieve the list of locale or language specific function names
2274
                $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2275
                foreach ($localeFunctions as $localeFunction) {
2276
                    list($localeFunction) = explode('##', $localeFunction); //    Strip out comments
2277
                    if (strpos($localeFunction, '=') !== false) {
2278
                        list($fName, $lfName) = explode('=', $localeFunction);
2279
                        $fName = trim($fName);
2280
                        $lfName = trim($lfName);
2281
                        if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
2282
                            self::$localeFunctions[$fName] = $lfName;
2283
                        }
2284
                    }
2285
                }
2286
                //    Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
2287
                if (isset(self::$localeFunctions['TRUE'])) {
2288
                    self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE'];
2289
                }
2290
                if (isset(self::$localeFunctions['FALSE'])) {
2291
                    self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
2292
                }
2293
2294
                $configFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'config';
2295
                if (!file_exists($configFile)) {
2296
                    $configFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . 'config';
2297
                }
2298
                if (file_exists($configFile)) {
2299
                    $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2300
                    foreach ($localeSettings as $localeSetting) {
2301
                        list($localeSetting) = explode('##', $localeSetting); //    Strip out comments
2302
                        if (strpos($localeSetting, '=') !== false) {
2303
                            list($settingName, $settingValue) = explode('=', $localeSetting);
2304
                            $settingName = strtoupper(trim($settingName));
2305
                            switch ($settingName) {
2306
                                case 'ARGUMENTSEPARATOR':
2307
                                    self::$localeArgumentSeparator = trim($settingValue);
2308
                                    break;
2309
                            }
2310
                        }
2311
                    }
2312
                }
2313
            }
2314
2315
            self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
2316
            self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
2317
            self::$localeLanguage = $locale;
2318
2319
            return true;
2320
        }
2321
2322
        return false;
2323
    }
2324
2325 1
    public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
2326
    {
2327 1
        $strlen = mb_strlen($formula);
2328 1
        for ($i = 0; $i < $strlen; ++$i) {
2329 1
            $chr = mb_substr($formula, $i, 1);
2330
            switch ($chr) {
2331 1
                case '{':
2332
                    $inBraces = true;
2333
                    break;
2334 1
                case '}':
2335
                    $inBraces = false;
2336
                    break;
2337 1
                case $fromSeparator:
2338
                    if (!$inBraces) {
2339
                        $formula = mb_substr($formula, 0, $i) . $toSeparator . mb_substr($formula, $i + 1);
2340
                    }
2341
            }
2342
        }
2343
2344 1
        return $formula;
2345
    }
2346
2347
    /**
2348
     * @param string $fromSeparator
2349
     * @param string $toSeparator
2350
     */
2351
    private static function translateFormula($from, $to, $formula, $fromSeparator, $toSeparator)
2352
    {
2353
        //    Convert any Excel function names to the required language
2354
        if (self::$localeLanguage !== 'en_us') {
2355
            $inBraces = false;
2356
            //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
2357
            if (strpos($formula, '"') !== false) {
2358
                //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
2359
                //        the formula
2360
                $temp = explode('"', $formula);
2361
                $i = false;
2362
                foreach ($temp as &$value) {
2363
                    //    Only count/replace in alternating array entries
2364 View Code Duplication
                    if ($i = !$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...
2365
                        $value = preg_replace($from, $to, $value);
2366
                        $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces);
2367
                    }
2368
                }
2369
                unset($value);
2370
                //    Then rebuild the formula string
2371
                $formula = implode('"', $temp);
2372 View Code Duplication
            } else {
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...
2373
                //    If there's no quoted strings, then we do a simple count/replace
2374
                $formula = preg_replace($from, $to, $formula);
2375
                $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inBraces);
2376
            }
2377
        }
2378
2379
        return $formula;
2380
    }
2381
2382
    private static $functionReplaceFromExcel = null;
2383
    private static $functionReplaceToLocale = null;
2384
2385 View Code Duplication
    public function _translateFormulaToLocale($formula)
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...
2386
    {
2387
        if (self::$functionReplaceFromExcel === null) {
2388
            self::$functionReplaceFromExcel = [];
2389
            foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2390
                self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelFunctionName) . '([\s]*\()/Ui';
2391
            }
2392
            foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2393
                self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelBoolean) . '([^\w\.])/Ui';
2394
            }
2395
        }
2396
2397
        if (self::$functionReplaceToLocale === null) {
2398
            self::$functionReplaceToLocale = [];
2399
            foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2400
                self::$functionReplaceToLocale[] = '$1' . trim($localeFunctionName) . '$2';
2401
            }
2402
            foreach (array_values(self::$localeBoolean) as $localeBoolean) {
2403
                self::$functionReplaceToLocale[] = '$1' . trim($localeBoolean) . '$2';
2404
            }
2405
        }
2406
2407
        return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
2408
    }
2409
2410
    private static $functionReplaceFromLocale = null;
2411
    private static $functionReplaceToExcel = null;
2412
2413 View Code Duplication
    public function _translateFormulaToEnglish($formula)
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...
2414
    {
2415
        if (self::$functionReplaceFromLocale === null) {
2416
            self::$functionReplaceFromLocale = [];
2417
            foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2418
                self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($localeFunctionName) . '([\s]*\()/Ui';
2419
            }
2420
            foreach (array_values(self::$localeBoolean) as $excelBoolean) {
2421
                self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($excelBoolean) . '([^\w\.])/Ui';
2422
            }
2423
        }
2424
2425
        if (self::$functionReplaceToExcel === null) {
2426
            self::$functionReplaceToExcel = [];
2427
            foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2428
                self::$functionReplaceToExcel[] = '$1' . trim($excelFunctionName) . '$2';
2429
            }
2430
            foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2431
                self::$functionReplaceToExcel[] = '$1' . trim($excelBoolean) . '$2';
2432
            }
2433
        }
2434
2435
        return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
2436
    }
2437
2438 21
    public static function localeFunc($function)
2439
    {
2440 21
        if (self::$localeLanguage !== 'en_us') {
2441
            $functionName = trim($function, '(');
2442
            if (isset(self::$localeFunctions[$functionName])) {
2443
                $brace = ($functionName != $function);
2444
                $function = self::$localeFunctions[$functionName];
2445
                if ($brace) {
2446
                    $function .= '(';
2447
                }
2448
            }
2449
        }
2450
2451 21
        return $function;
2452
    }
2453
2454
    /**
2455
     * Wrap string values in quotes
2456
     *
2457
     * @param mixed $value
2458
     * @return mixed
2459
     */
2460 55
    public static function wrapResult($value)
2461
    {
2462 55
        if (is_string($value)) {
2463
            //    Error values cannot be "wrapped"
2464 45
            if (preg_match('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
2465
                //    Return Excel errors "as is"
2466 1
                return $value;
2467
            }
2468
            //    Return strings wrapped in quotes
2469 45
            return '"' . $value . '"';
2470
        //    Convert numeric errors to NaN error
2471 20
        } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2472
            return Calculation\Functions::NAN();
2473
        }
2474
2475 20
        return $value;
2476
    }
2477
2478
    /**
2479
     * Remove quotes used as a wrapper to identify string values
2480
     *
2481
     * @param mixed $value
2482
     * @return mixed
2483
     */
2484 67
    public static function unwrapResult($value)
2485
    {
2486 67
        if (is_string($value)) {
2487 49
            if ((isset($value{0})) && ($value{0} == '"') && (substr($value, -1) == '"')) {
2488 49
                return substr($value, 1, -1);
2489
            }
2490
        //    Convert numeric errors to NAN error
2491 33
        } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2492
            return Calculation\Functions::NAN();
2493
        }
2494
2495 33
        return $value;
2496
    }
2497
2498
    /**
2499
     * Calculate cell value (using formula from a cell ID)
2500
     * Retained for backward compatibility
2501
     *
2502
     * @param    Cell    $pCell    Cell to calculate
2503
     * @throws    Calculation\Exception
2504
     * @return    mixed
2505
     */
2506
    public function calculate(Cell $pCell = null)
2507
    {
2508
        try {
2509
            return $this->calculateCellValue($pCell);
2510
        } catch (Exception $e) {
2511
            throw new Calculation\Exception($e->getMessage());
2512
        }
2513
    }
2514
2515
    /**
2516
     * Calculate the value of a cell formula
2517
     *
2518
     * @param    Cell    $pCell        Cell to calculate
2519
     * @param    bool            $resetLog    Flag indicating whether the debug log should be reset or not
2520
     * @throws    Calculation\Exception
2521
     * @return    mixed
2522
     */
2523 22
    public function calculateCellValue(Cell $pCell = null, $resetLog = true)
2524
    {
2525 22
        if ($pCell === null) {
2526
            return null;
2527
        }
2528
2529 22
        $returnArrayAsType = self::$returnArrayAsType;
2530 22
        if ($resetLog) {
2531
            //    Initialise the logging settings if requested
2532 22
            $this->formulaError = null;
2533 22
            $this->_debugLog->clearLog();
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2534 22
            $this->cyclicReferenceStack->clear();
0 ignored issues
show
Bug introduced by
The method clear cannot be called on $this->cyclicReferenceStack (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2535 22
            $this->cyclicFormulaCounter = 1;
2536
2537 22
            self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
2538
        }
2539
2540
        //    Execute the calculation for the cell formula
2541 22
        $this->cellStack[] = [
2542 22
            'sheet' => $pCell->getWorksheet()->getTitle(),
2543 22
            'cell' => $pCell->getCoordinate(),
2544
        ];
2545
        try {
2546 22
            $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
2547 22
            $cellAddress = array_pop($this->cellStack);
2548 22
            $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2549
        } catch (Exception $e) {
2550
            $cellAddress = array_pop($this->cellStack);
2551
            $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2552
            throw new Calculation\Exception($e->getMessage());
2553
        }
2554
2555 22
        if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
2556 1
            self::$returnArrayAsType = $returnArrayAsType;
2557 1
            $testResult = Calculation\Functions::flattenArray($result);
2558 1
            if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
2559
                return Calculation\Functions::VALUE();
2560
            }
2561
            //    If there's only a single cell in the array, then we allow it
2562 1
            if (count($testResult) != 1) {
2563
                //    If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
2564
                $r = array_keys($result);
2565
                $r = array_shift($r);
2566
                if (!is_numeric($r)) {
2567
                    return Calculation\Functions::VALUE();
2568
                }
2569
                if (is_array($result[$r])) {
2570
                    $c = array_keys($result[$r]);
2571
                    $c = array_shift($c);
2572
                    if (!is_numeric($c)) {
2573
                        return Calculation\Functions::VALUE();
2574
                    }
2575
                }
2576
            }
2577 1
            $result = array_shift($testResult);
2578
        }
2579 22
        self::$returnArrayAsType = $returnArrayAsType;
2580
2581 22
        if ($result === null) {
2582
            return 0;
2583 22
        } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
2584
            return Calculation\Functions::NAN();
2585
        }
2586
2587 22
        return $result;
2588
    }
2589
2590
    /**
2591
     * Validate and parse a formula string
2592
     *
2593
     * @param    string        $formula        Formula to parse
2594
     * @throws    Calculation\Exception
2595
     * @return    array
2596
     */
2597
    public function parseFormula($formula)
2598
    {
2599
        //    Basic validation that this is indeed a formula
2600
        //    We return an empty array if not
2601
        $formula = trim($formula);
2602
        if ((!isset($formula{0})) || ($formula{0} != '=')) {
2603
            return [];
2604
        }
2605
        $formula = ltrim(substr($formula, 1));
2606
        if (!isset($formula{0})) {
2607
            return [];
2608
        }
2609
2610
        //    Parse the formula and return the token stack
2611
        return $this->_parseFormula($formula);
2612
    }
2613
2614
    /**
2615
     * Calculate the value of a formula
2616
     *
2617
     * @param    string            $formula    Formula to parse
2618
     * @param    string            $cellID        Address of the cell to calculate
2619
     * @param    Cell    $pCell        Cell to calculate
2620
     * @throws    Calculation\Exception
2621
     * @return    mixed
2622
     */
2623
    public function calculateFormula($formula, $cellID = null, Cell $pCell = null)
2624
    {
2625
        //    Initialise the logging settings
2626
        $this->formulaError = null;
2627
        $this->_debugLog->clearLog();
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2628
        $this->cyclicReferenceStack->clear();
0 ignored issues
show
Bug introduced by
The method clear cannot be called on $this->cyclicReferenceStack (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2629
2630
        if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
2631
            $cellID = 'A1';
2632
            $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
2633
        } else {
2634
            //    Disable calculation cacheing because it only applies to cell calculations, not straight formulae
2635
            //    But don't actually flush any cache
2636
            $resetCache = $this->getCalculationCacheEnabled();
2637
            $this->calculationCacheEnabled = false;
2638
        }
2639
2640
        //    Execute the calculation
2641
        try {
2642
            $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
2643
        } catch (Exception $e) {
2644
            throw new Calculation\Exception($e->getMessage());
2645
        }
2646
2647
        if ($this->spreadsheet === null) {
2648
            //    Reset calculation cacheing to its previous state
2649
            $this->calculationCacheEnabled = $resetCache;
0 ignored issues
show
Bug introduced by
The variable $resetCache 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...
2650
        }
2651
2652
        return $result;
2653
    }
2654
2655 22
    public function getValueFromCache($cellReference, &$cellValue)
2656
    {
2657
        // Is calculation cacheing enabled?
2658
        // Is the value present in calculation cache?
2659 22
        $this->_debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2660 22
        if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
2661 21
            $this->_debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2662
            // Return the cached result
2663 21
            $cellValue = $this->calculationCache[$cellReference];
2664
2665 21
            return true;
2666
        }
2667
2668 22
        return false;
2669
    }
2670
2671
    /**
2672
     * @param string $cellReference
2673
     */
2674 22
    public function saveValueToCache($cellReference, $cellValue)
2675
    {
2676 22
        if ($this->calculationCacheEnabled) {
2677 22
            $this->calculationCache[$cellReference] = $cellValue;
2678
        }
2679 22
    }
2680
2681
    /**
2682
     * Parse a cell formula and calculate its value
2683
     *
2684
     * @param    string            $formula    The formula to parse and calculate
2685
     * @param    string            $cellID        The ID (e.g. A3) of the cell that we are calculating
2686
     * @param    Cell    $pCell        Cell to calculate
2687
     * @throws   Calculation\Exception
2688
     * @return   mixed
2689
     */
2690 91
    public function _calculateFormulaValue($formula, $cellID = null, Cell $pCell = null)
2691
    {
2692 91
        $cellValue = null;
2693
2694
        //    Basic validation that this is indeed a formula
2695
        //    We simply return the cell value if not
2696 91
        $formula = trim($formula);
2697 91
        if ($formula{0} != '=') {
2698
            return self::wrapResult($formula);
2699
        }
2700 91
        $formula = ltrim(substr($formula, 1));
2701 91
        if (!isset($formula{0})) {
2702
            return self::wrapResult($formula);
2703
        }
2704
2705 91
        $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
2706 91
        $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
2707 91
        $wsCellReference = $wsTitle . '!' . $cellID;
2708
2709 91
        if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
2710 21
            return $cellValue;
2711
        }
2712
2713 91
        if (($wsTitle{0} !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
0 ignored issues
show
Bug introduced by
The method onStack cannot be called on $this->cyclicReferenceStack (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2714
            if ($this->cyclicFormulaCount <= 0) {
2715
                $this->cyclicFormulaCell = '';
2716
2717
                return $this->raiseFormulaError('Cyclic Reference in Formula');
2718
            } elseif ($this->cyclicFormulaCell === $wsCellReference) {
2719
                ++$this->cyclicFormulaCounter;
2720
                if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2721
                    $this->cyclicFormulaCell = '';
2722
2723
                    return $cellValue;
2724
                }
2725
            } elseif ($this->cyclicFormulaCell == '') {
2726
                if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2727
                    return $cellValue;
2728
                }
2729
                $this->cyclicFormulaCell = $wsCellReference;
2730
            }
2731
        }
2732
2733
        //    Parse the formula onto the token stack and calculate the value
2734 91
        $this->cyclicReferenceStack->push($wsCellReference);
0 ignored issues
show
Bug introduced by
The method push cannot be called on $this->cyclicReferenceStack (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2735 91
        $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
2736 91
        $this->cyclicReferenceStack->pop();
0 ignored issues
show
Bug introduced by
The method pop cannot be called on $this->cyclicReferenceStack (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
2737
2738
        // Save to calculation cache
2739 91
        if ($cellID !== null) {
2740 22
            $this->saveValueToCache($wsCellReference, $cellValue);
2741
        }
2742
2743
        //    Return the calculated value
2744 91
        return $cellValue;
2745
    }
2746
2747
    /**
2748
     * Ensure that paired matrix operands are both matrices and of the same size
2749
     *
2750
     * @param    mixed        &$operand1    First matrix operand
2751
     * @param    mixed        &$operand2    Second matrix operand
2752
     * @param    int        $resize        Flag indicating whether the matrices should be resized to match
2753
     *                                        and (if so), whether the smaller dimension should grow or the
2754
     *                                        larger should shrink.
2755
     *                                            0 = no resize
2756
     *                                            1 = shrink to fit
2757
     *                                            2 = extend to fit
2758
     */
2759 5
    private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1)
2760
    {
2761
        //    Examine each of the two operands, and turn them into an array if they aren't one already
2762
        //    Note that this function should only be called if one or both of the operand is already an array
2763 5
        if (!is_array($operand1)) {
2764
            list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2);
2765
            $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
2766
            $resize = 0;
2767 5
        } elseif (!is_array($operand2)) {
2768 2
            list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1);
2769 2
            $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
2770 2
            $resize = 0;
2771
        }
2772
2773 5
        list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1);
2774 5
        list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2);
2775 5
        if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
2776 5
            $resize = 1;
2777
        }
2778
2779 5
        if ($resize == 2) {
2780
            //    Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
2781
            self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2782 5
        } elseif ($resize == 1) {
2783
            //    Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
2784 5
            self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2785
        }
2786
2787 5
        return [$matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns];
2788
    }
2789
2790
    /**
2791
     * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
2792
     *
2793
     * @param    mixed        &$matrix        matrix operand
2794
     * @return    int[]        An array comprising the number of rows, and number of columns
2795
     */
2796 5
    private static function getMatrixDimensions(&$matrix)
2797
    {
2798 5
        $matrixRows = count($matrix);
2799 5
        $matrixColumns = 0;
2800 5
        foreach ($matrix as $rowKey => $rowValue) {
2801 5
            $matrixColumns = max(count($rowValue), $matrixColumns);
2802 5
            if (!is_array($rowValue)) {
2803
                $matrix[$rowKey] = [$rowValue];
2804
            } else {
2805 5
                $matrix[$rowKey] = array_values($rowValue);
2806
            }
2807
        }
2808 5
        $matrix = array_values($matrix);
2809
2810 5
        return [$matrixRows, $matrixColumns];
2811
    }
2812
2813
    /**
2814
     * Ensure that paired matrix operands are both matrices of the same size
2815
     *
2816
     * @param    mixed        &$matrix1        First matrix operand
2817
     * @param    mixed        &$matrix2        Second matrix operand
2818
     * @param    int        $matrix1Rows    Row size of first matrix operand
2819
     * @param    int        $matrix1Columns    Column size of first matrix operand
2820
     * @param    int        $matrix2Rows    Row size of second matrix operand
2821
     * @param    int        $matrix2Columns    Column size of second matrix operand
2822
     */
2823 5
    private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2824
    {
2825 5 View Code Duplication
        if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
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...
2826
            if ($matrix2Rows < $matrix1Rows) {
2827
                for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
2828
                    unset($matrix1[$i]);
2829
                }
2830
            }
2831
            if ($matrix2Columns < $matrix1Columns) {
2832
                for ($i = 0; $i < $matrix1Rows; ++$i) {
2833
                    for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2834
                        unset($matrix1[$i][$j]);
2835
                    }
2836
                }
2837
            }
2838
        }
2839
2840 5 View Code Duplication
        if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
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...
2841
            if ($matrix1Rows < $matrix2Rows) {
2842
                for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
2843
                    unset($matrix2[$i]);
2844
                }
2845
            }
2846
            if ($matrix1Columns < $matrix2Columns) {
2847
                for ($i = 0; $i < $matrix2Rows; ++$i) {
2848
                    for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2849
                        unset($matrix2[$i][$j]);
2850
                    }
2851
                }
2852
            }
2853
        }
2854 5
    }
2855
2856
    /**
2857
     * Ensure that paired matrix operands are both matrices of the same size
2858
     *
2859
     * @param    mixed        &$matrix1    First matrix operand
2860
     * @param    mixed        &$matrix2    Second matrix operand
2861
     * @param    int        $matrix1Rows    Row size of first matrix operand
2862
     * @param    int        $matrix1Columns    Column size of first matrix operand
2863
     * @param    int        $matrix2Rows    Row size of second matrix operand
2864
     * @param    int        $matrix2Columns    Column size of second matrix operand
2865
     */
2866
    private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2867
    {
2868 View Code Duplication
        if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
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...
2869
            if ($matrix2Columns < $matrix1Columns) {
2870
                for ($i = 0; $i < $matrix2Rows; ++$i) {
2871
                    $x = $matrix2[$i][$matrix2Columns - 1];
2872
                    for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2873
                        $matrix2[$i][$j] = $x;
2874
                    }
2875
                }
2876
            }
2877
            if ($matrix2Rows < $matrix1Rows) {
2878
                $x = $matrix2[$matrix2Rows - 1];
2879
                for ($i = 0; $i < $matrix1Rows; ++$i) {
2880
                    $matrix2[$i] = $x;
2881
                }
2882
            }
2883
        }
2884
2885 View Code Duplication
        if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
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...
2886
            if ($matrix1Columns < $matrix2Columns) {
2887
                for ($i = 0; $i < $matrix1Rows; ++$i) {
2888
                    $x = $matrix1[$i][$matrix1Columns - 1];
2889
                    for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2890
                        $matrix1[$i][$j] = $x;
2891
                    }
2892
                }
2893
            }
2894
            if ($matrix1Rows < $matrix2Rows) {
2895
                $x = $matrix1[$matrix1Rows - 1];
2896
                for ($i = 0; $i < $matrix2Rows; ++$i) {
2897
                    $matrix1[$i] = $x;
2898
                }
2899
            }
2900
        }
2901
    }
2902
2903
    /**
2904
     * Format details of an operand for display in the log (based on operand type)
2905
     *
2906
     * @param    mixed        $value    First matrix operand
2907
     * @return    mixed
2908
     */
2909 90
    private function showValue($value)
2910
    {
2911 90
        if ($this->_debugLog->getWriteDebugLog()) {
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2912
            $testArray = Calculation\Functions::flattenArray($value);
2913
            if (count($testArray) == 1) {
2914
                $value = array_pop($testArray);
2915
            }
2916
2917
            if (is_array($value)) {
2918
                $returnMatrix = [];
2919
                $pad = $rpad = ', ';
2920
                foreach ($value as $row) {
2921
                    if (is_array($row)) {
2922
                        $returnMatrix[] = implode($pad, array_map([$this, 'showValue'], $row));
2923
                        $rpad = '; ';
2924
                    } else {
2925
                        $returnMatrix[] = $this->showValue($row);
2926
                    }
2927
                }
2928
2929
                return '{ ' . implode($rpad, $returnMatrix) . ' }';
2930
            } elseif (is_string($value) && (trim($value, '"') == $value)) {
2931
                return '"' . $value . '"';
2932
            } elseif (is_bool($value)) {
2933
                return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
2934
            }
2935
        }
2936
2937 90
        return Calculation\Functions::flattenSingleValue($value);
2938
    }
2939
2940
    /**
2941
     * Format type and details of an operand for display in the log (based on operand type)
2942
     *
2943
     * @param    mixed        $value    First matrix operand
2944
     * @return    string|null
2945
     */
2946 90
    private function showTypeDetails($value)
2947
    {
2948 90
        if ($this->_debugLog->getWriteDebugLog()) {
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
2949
            $testArray = Calculation\Functions::flattenArray($value);
2950
            if (count($testArray) == 1) {
2951
                $value = array_pop($testArray);
2952
            }
2953
2954
            if ($value === null) {
2955
                return 'a NULL value';
2956
            } elseif (is_float($value)) {
2957
                $typeString = 'a floating point number';
2958
            } elseif (is_int($value)) {
2959
                $typeString = 'an integer number';
2960
            } elseif (is_bool($value)) {
2961
                $typeString = 'a boolean';
2962
            } elseif (is_array($value)) {
2963
                $typeString = 'a matrix';
2964
            } else {
2965
                if ($value == '') {
2966
                    return 'an empty string';
2967
                } elseif ($value{0} == '#') {
2968
                    return 'a ' . $value . ' error';
2969
                } else {
2970
                    $typeString = 'a string';
2971
                }
2972
            }
2973
2974
            return $typeString . ' with a value of ' . $this->showValue($value);
2975
        }
2976 90
    }
2977
2978 91
    private function convertMatrixReferences($formula)
2979
    {
2980 91
        static $matrixReplaceFrom = ['{', ';', '}'];
2981 91
        static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'];
2982
2983
        //    Convert any Excel matrix references to the MKMATRIX() function
2984 91
        if (strpos($formula, '{') !== false) {
2985
            //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
2986
            if (strpos($formula, '"') !== false) {
2987
                //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
2988
                //        the formula
2989
                $temp = explode('"', $formula);
2990
                //    Open and Closed counts used for trapping mismatched braces in the formula
2991
                $openCount = $closeCount = 0;
2992
                $i = false;
2993
                foreach ($temp as &$value) {
2994
                    //    Only count/replace in alternating array entries
2995
                    if ($i = !$i) {
2996
                        $openCount += substr_count($value, '{');
2997
                        $closeCount += substr_count($value, '}');
2998
                        $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
2999
                    }
3000
                }
3001
                unset($value);
3002
                //    Then rebuild the formula string
3003
                $formula = implode('"', $temp);
3004
            } else {
3005
                //    If there's no quoted strings, then we do a simple count/replace
3006
                $openCount = substr_count($formula, '{');
3007
                $closeCount = substr_count($formula, '}');
3008
                $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
3009
            }
3010
            //    Trap for mismatched braces and trigger an appropriate error
3011
            if ($openCount < $closeCount) {
3012
                if ($openCount > 0) {
3013
                    return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
3014
                } else {
3015
                    return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered");
3016
                }
3017
            } elseif ($openCount > $closeCount) {
3018
                if ($closeCount > 0) {
3019
                    return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
3020
                } else {
3021
                    return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered");
3022
                }
3023
            }
3024
        }
3025
3026 91
        return $formula;
3027
    }
3028
3029
    private static function mkMatrix()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
3030
    {
3031
        return func_get_args();
3032
    }
3033
3034
    //    Binary Operators
3035
    //    These operators always work on two values
3036
    //    Array key is the operator, the value indicates whether this is a left or right associative operator
3037
    private static $operatorAssociativity = [
3038
        '^' => 0, //    Exponentiation
3039
        '*' => 0, '/' => 0, //    Multiplication and Division
3040
        '+' => 0, '-' => 0, //    Addition and Subtraction
3041
        '&' => 0, //    Concatenation
3042
        '|' => 0, ':' => 0, //    Intersect and Range
3043
        '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, //    Comparison
3044
    ];
3045
3046
    //    Comparison (Boolean) Operators
3047
    //    These operators work on two values, but always return a boolean result
3048
    private static $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true];
3049
3050
    //    Operator Precedence
3051
    //    This list includes all valid operators, whether binary (including boolean) or unary (such as %)
3052
    //    Array key is the operator, the value is its precedence
3053
    private static $operatorPrecedence = [
3054
        ':' => 8, //    Range
3055
        '|' => 7, //    Intersect
3056
        '~' => 6, //    Negation
3057
        '%' => 5, //    Percentage
3058
        '^' => 4, //    Exponentiation
3059
        '*' => 3, '/' => 3, //    Multiplication and Division
3060
        '+' => 2, '-' => 2, //    Addition and Subtraction
3061
        '&' => 1, //    Concatenation
3062
        '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, //    Comparison
3063
    ];
3064
3065
    // Convert infix to postfix notation
3066 91
    private function _parseFormula($formula, Cell $pCell = null)
3067
    {
3068 91
        if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
3069
            return false;
3070
        }
3071
3072
        //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
3073
        //        so we store the parent worksheet so that we can re-attach it when necessary
3074 91
        $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
3075
3076 91
        $regexpMatchString = '/^(' . self::CALCULATION_REGEXP_FUNCTION .
3077 91
                                '|' . self::CALCULATION_REGEXP_CELLREF .
3078 91
                                '|' . self::CALCULATION_REGEXP_NUMBER .
3079 91
                                '|' . self::CALCULATION_REGEXP_STRING .
3080 91
                                '|' . self::CALCULATION_REGEXP_OPENBRACE .
3081 91
                                '|' . self::CALCULATION_REGEXP_NAMEDRANGE .
3082 91
                                '|' . self::CALCULATION_REGEXP_ERROR .
3083 91
                                ')/si';
3084
3085
        //    Start with initialisation
3086 91
        $index = 0;
3087 91
        $stack = new Calculation\Token\Stack();
3088 91
        $output = [];
3089 91
        $expectingOperator = false; //    We use this test in syntax-checking the expression to determine when a
3090
                                                    //        - is a negation or + is a positive operator rather than an operation
3091 91
        $expectingOperand = false; //    We use this test in syntax-checking the expression to determine whether an operand
3092
                                                    //        should be null in a function call
3093
        //    The guts of the lexical parser
3094
        //    Loop through the formula extracting each operator and operand in turn
3095 91
        while (true) {
3096 91
            $opCharacter = $formula{$index};    //    Get the first character of the value at the current index position
3097 91
            if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula{$index + 1}]))) {
3098 31
                $opCharacter .= $formula{++$index};
3099
            }
3100
3101
            //    Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
3102 91
            $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
3103
3104 91
            if ($opCharacter == '-' && !$expectingOperator) {                //    Is it a negation instead of a minus?
3105 1
                $stack->push('Unary Operator', '~'); //    Put a negation on the stack
3106 1
                ++$index; //        and drop the negation symbol
3107 91
            } elseif ($opCharacter == '%' && $expectingOperator) {
3108
                $stack->push('Unary Operator', '%'); //    Put a percentage on the stack
3109
                ++$index;
3110 91
            } elseif ($opCharacter == '+' && !$expectingOperator) {            //    Positive (unary plus rather than binary operator plus) can be discarded?
3111
                ++$index; //    Drop the redundant plus symbol
3112 91
            } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) {    //    We have to explicitly deny a tilde or pipe, because they are legal
3113
                return $this->raiseFormulaError("Formula Error: Illegal character '~'"); //        on the stack but not in the input expression
3114 91
            } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) {    //    Are we putting an operator on the stack?
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...
3115 89 View Code Duplication
                while ($stack->count() > 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...
3116 89
                    ($o2 = $stack->last()) &&
3117 89
                    isset(self::$operators[$o2['value']]) &&
3118 89
                    @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3119 1
                    $output[] = $stack->pop(); //    Swap operands and higher precedence operators from the stack to the output
3120
                }
3121 89
                $stack->push('Binary Operator', $opCharacter); //    Finally put our current operator onto the stack
3122 89
                ++$index;
3123 89
                $expectingOperator = false;
3124 91
            } elseif ($opCharacter == ')' && $expectingOperator) {            //    Are we expecting to close a parenthesis?
3125 21
                $expectingOperand = false;
3126 21 View Code Duplication
                while (($o2 = $stack->pop()) && $o2['value'] != '(') {        //    Pop off the stack back to the last (
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...
3127 20
                    if ($o2 === null) {
3128
                        return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"');
3129
                    } else {
3130 20
                        $output[] = $o2;
3131
                    }
3132
                }
3133 21
                $d = $stack->last(2);
3134 21
                if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) {    //    Did this parenthesis just close a function?
3135 21
                    $functionName = $matches[1]; //    Get the function name
3136 21
                    $d = $stack->pop();
3137 21
                    $argumentCount = $d['value']; //    See how many arguments there were (argument count is the next value stored on the stack)
3138 21
                    $output[] = $d; //    Dump the argument count on the output
3139 21
                    $output[] = $stack->pop(); //    Pop the function and push onto the output
3140 21
                    if (isset(self::$controlFunctions[$functionName])) {
3141
                        $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
3142
                        $functionCall = self::$controlFunctions[$functionName]['functionCall'];
0 ignored issues
show
Unused Code introduced by
$functionCall 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...
3143 21
                    } elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) {
3144 21
                        $expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount'];
3145 21
                        $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
0 ignored issues
show
Unused Code introduced by
$functionCall 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...
3146
                    } else {    // did we somehow push a non-function on the stack? this should never happen
3147
                        return $this->raiseFormulaError('Formula Error: Internal error, non-function on stack');
3148
                    }
3149
                    //    Check the argument count
3150 21
                    $argumentCountError = false;
3151 21
                    if (is_numeric($expectedArgumentCount)) {
3152 5
                        if ($expectedArgumentCount < 0) {
3153
                            if ($argumentCount > abs($expectedArgumentCount)) {
3154
                                $argumentCountError = true;
3155
                                $expectedArgumentCountString = 'no more than ' . abs($expectedArgumentCount);
3156
                            }
3157
                        } else {
3158 5
                            if ($argumentCount != $expectedArgumentCount) {
3159
                                $argumentCountError = true;
3160 5
                                $expectedArgumentCountString = $expectedArgumentCount;
3161
                            }
3162
                        }
3163 20
                    } elseif ($expectedArgumentCount != '*') {
3164 20
                        $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
0 ignored issues
show
Unused Code introduced by
$isOperandOrFunction 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...
3165 20
                        switch ($argMatch[2]) {
3166 20
                            case '+':
3167 19
                                if ($argumentCount < $argMatch[1]) {
3168
                                    $argumentCountError = true;
3169
                                    $expectedArgumentCountString = $argMatch[1] . ' or more ';
3170
                                }
3171 19
                                break;
3172 11 View Code Duplication
                            case '-':
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...
3173 9
                                if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
3174
                                    $argumentCountError = true;
3175
                                    $expectedArgumentCountString = 'between ' . $argMatch[1] . ' and ' . $argMatch[3];
3176
                                }
3177 9
                                break;
3178 2 View Code Duplication
                            case ',':
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...
3179 2
                                if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
3180
                                    $argumentCountError = true;
3181
                                    $expectedArgumentCountString = 'either ' . $argMatch[1] . ' or ' . $argMatch[3];
3182
                                }
3183 2
                                break;
3184
                        }
3185
                    }
3186 21
                    if ($argumentCountError) {
3187
                        return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, " . $expectedArgumentCountString . ' expected');
0 ignored issues
show
Bug introduced by
The variable $expectedArgumentCountString 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...
3188
                    }
3189
                }
3190 21
                ++$index;
3191 91
            } elseif ($opCharacter == ',') {            //    Is this the separator for function arguments?
3192 12 View Code Duplication
                while (($o2 = $stack->pop()) && $o2['value'] != '(') {        //    Pop off the stack back to the last (
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...
3193 10
                    if ($o2 === null) {
3194
                        return $this->raiseFormulaError('Formula Error: Unexpected ,');
3195
                    } else {
3196 10
                        $output[] = $o2; // pop the argument expression stuff and push onto the output
3197
                    }
3198
                }
3199
                //    If we've a comma when we're expecting an operand, then what we actually have is a null operand;
3200
                //        so push a null onto the stack
3201 12
                if (($expectingOperand) || (!$expectingOperator)) {
3202
                    $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
3203
                }
3204
                // make sure there was a function
3205 12
                $d = $stack->last(2);
3206 12
                if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) {
3207
                    return $this->raiseFormulaError('Formula Error: Unexpected ,');
3208
                }
3209 12
                $d = $stack->pop();
3210 12
                $stack->push($d['type'], ++$d['value'], $d['reference']); // increment the argument count
3211 12
                $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
3212 12
                $expectingOperator = false;
3213 12
                $expectingOperand = true;
3214 12
                ++$index;
3215 91
            } elseif ($opCharacter == '(' && !$expectingOperator) {
3216 2
                $stack->push('Brace', '(');
3217 2
                ++$index;
3218 91
            } elseif ($isOperandOrFunction && !$expectingOperator) {    // do we now have a function/variable/number?
3219 91
                $expectingOperator = true;
3220 91
                $expectingOperand = false;
3221 91
                $val = $match[1];
3222 91
                $length = strlen($val);
3223
3224 91
                if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) {
3225 21
                    $val = preg_replace('/\s/u', '', $val);
3226 21
                    if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) {    // it's a function
3227 21
                        $stack->push('Function', strtoupper($val));
3228 21
                        $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index + $length), $amatch);
3229 21
                        if ($ax) {
3230 4
                            $stack->push('Operand Count for Function ' . strtoupper($val) . ')', 0);
3231 4
                            $expectingOperator = true;
3232
                        } else {
3233 21
                            $stack->push('Operand Count for Function ' . strtoupper($val) . ')', 1);
3234 21
                            $expectingOperator = false;
3235
                        }
3236 21
                        $stack->push('Brace', '(');
3237
                    } else {    // it's a var w/ implicit multiplication
3238 21
                        $output[] = ['type' => 'Value', 'value' => $matches[1], 'reference' => null];
3239
                    }
3240 91
                } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $val, $matches)) {
3241
                    //    Watch for this case-change when modifying to allow cell references in different worksheets...
3242
                    //    Should only be applied to the actual cell column, not the worksheet name
3243
3244
                    //    If the last entry on the stack was a : operator, then we have a cell range reference
3245 33
                    $testPrevOp = $stack->last(1);
3246 33
                    if ($testPrevOp['value'] == ':') {
3247
                        //    If we have a worksheet reference, then we're playing with a 3D reference
3248 32
                        if ($matches[2] == '') {
3249
                            //    Otherwise, we 'inherit' the worksheet reference from the start cell reference
3250
                            //    The start of the cell range reference should be the last entry in $output
3251 32
                            $startCellRef = $output[count($output) - 1]['value'];
3252 32
                            preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $startCellRef, $startMatches);
3253 32
                            if ($startMatches[2] > '') {
3254 32
                                $val = $startMatches[2] . '!' . $val;
3255
                            }
3256
                        } else {
3257
                            return $this->raiseFormulaError('3D Range references are not yet supported');
3258
                        }
3259
                    }
3260
3261 33
                    $output[] = ['type' => 'Cell Reference', 'value' => $val, 'reference' => $val];
3262
                } else {    // it's a variable, constant, string, number or boolean
3263
                    //    If the last entry on the stack was a : operator, then we may have a row or column range reference
3264 74
                    $testPrevOp = $stack->last(1);
3265 74
                    if ($testPrevOp['value'] == ':') {
3266
                        $startRowColRef = $output[count($output) - 1]['value'];
3267
                        $rangeWS1 = '';
3268
                        if (strpos('!', $startRowColRef) !== false) {
3269
                            list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef);
3270
                        }
3271
                        if ($rangeWS1 != '') {
3272
                            $rangeWS1 .= '!';
3273
                        }
3274
                        $rangeWS2 = $rangeWS1;
3275
                        if (strpos('!', $val) !== false) {
3276
                            list($rangeWS2, $val) = explode('!', $val);
3277
                        }
3278
                        if ($rangeWS2 != '') {
3279
                            $rangeWS2 .= '!';
3280
                        }
3281
                        if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
3282
                            ($startRowColRef <= 1048576) && ($val <= 1048576)) {
3283
                            //    Row range
3284
                            $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD'; //    Max 16,384 columns for Excel2007
3285
                            $output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef;
3286
                            $val = $rangeWS2 . $endRowColRef . $val;
3287
                        } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
3288
                            (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
3289
                            //    Column range
3290
                            $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; //    Max 1,048,576 rows for Excel2007
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...
3291
                            $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1';
3292
                            $val = $rangeWS2 . $val . $endRowColRef;
3293
                        }
3294
                    }
3295
3296 74
                    $localeConstant = false;
3297 74
                    if ($opCharacter == '"') {
3298
                        //    UnEscape any quotes within the string
3299 45
                        $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val)));
3300 42
                    } elseif (is_numeric($val)) {
3301 40
                        if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
3302 16
                            $val = (float) $val;
3303
                        } else {
3304 40
                            $val = (integer) $val;
3305
                        }
3306 5
                    } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
3307 1
                        $excelConstant = trim(strtoupper($val));
3308 1
                        $val = self::$excelConstants[$excelConstant];
3309 4
                    } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
3310
                        $val = self::$excelConstants[$localeConstant];
3311
                    }
3312 74
                    $details = ['type' => 'Value', 'value' => $val, 'reference' => null];
3313 74
                    if ($localeConstant) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $localeConstant of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
3314
                        $details['localeValue'] = $localeConstant;
3315
                    }
3316 74
                    $output[] = $details;
3317
                }
3318 91
                $index += $length;
3319
            } elseif ($opCharacter == '$') {    // absolute row or column range
3320
                ++$index;
3321
            } elseif ($opCharacter == ')') {    // miscellaneous error checking
3322
                if ($expectingOperand) {
3323
                    $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
3324
                    $expectingOperand = false;
3325
                    $expectingOperator = true;
3326
                } else {
3327
                    return $this->raiseFormulaError("Formula Error: Unexpected ')'");
3328
                }
3329
            } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
3330
                return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
3331
            } else {    // I don't even want to know what you did to get here
3332
                return $this->raiseFormulaError('Formula Error: An unexpected error occured');
3333
            }
3334
            //    Test for end of formula string
3335 91
            if ($index == strlen($formula)) {
3336
                //    Did we end with an operator?.
3337
                //    Only valid for the % unary operator
3338 91
                if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
3339
                    return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
3340
                } else {
3341 91
                    break;
3342
                }
3343
            }
3344
            //    Ignore white space
3345 90
            while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
3346
                ++$index;
3347
            }
3348 90
            if ($formula{$index} == ' ') {
3349 53
                while ($formula{$index} == ' ') {
3350 53
                    ++$index;
3351
                }
3352
                //    If we're expecting an operator, but only have a space between the previous and next operands (and both are
3353
                //        Cell References) then we have an INTERSECTION operator
3354 53
                if (($expectingOperator) && (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
3355 53
                    ($output[count($output) - 1]['type'] == 'Cell Reference')) {
3356 View Code Duplication
                    while ($stack->count() > 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...
3357
                        ($o2 = $stack->last()) &&
3358
                        isset(self::$operators[$o2['value']]) &&
3359
                        @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3360
                        $output[] = $stack->pop(); //    Swap operands and higher precedence operators from the stack to the output
3361
                    }
3362
                    $stack->push('Binary Operator', '|'); //    Put an Intersect Operator on the stack
3363
                    $expectingOperator = false;
3364
                }
3365
            }
3366
        }
3367
3368 91 View Code Duplication
        while (($op = $stack->pop()) !== null) {    // pop everything off the stack and push onto output
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...
3369 85
            if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
3370
                return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
3371
            }
3372 85
            $output[] = $op;
3373
        }
3374
3375 91
        return $output;
3376
    }
3377
3378 89
    private static function dataTestReference(&$operandData)
3379
    {
3380 89
        $operand = $operandData['value'];
3381 89
        if (($operandData['reference'] === null) && (is_array($operand))) {
3382 1
            $rKeys = array_keys($operand);
3383 1
            $rowKey = array_shift($rKeys);
3384 1
            $cKeys = array_keys(array_keys($operand[$rowKey]));
3385 1
            $colKey = array_shift($cKeys);
3386 1
            if (ctype_upper($colKey)) {
3387
                $operandData['reference'] = $colKey . $rowKey;
3388
            }
3389
        }
3390
3391 89
        return $operand;
3392
    }
3393
3394
    // evaluate postfix notation
3395
3396
    /**
3397
     * @param string $cellID
3398
     */
3399 91
    private function processTokenStack($tokens, $cellID = null, Cell $pCell = null)
3400
    {
3401 91
        if ($tokens == false) {
3402
            return false;
3403
        }
3404
3405
        //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
3406
        //        so we store the parent cell collection so that we can re-attach it when necessary
3407 91
        $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
3408 91
        $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
3409 91
        $stack = new Calculation\Token\Stack();
3410
3411
        //    Loop through each token in turn
3412 91
        foreach ($tokens as $tokenData) {
3413 91
            $token = $tokenData['value'];
3414
            // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
3415 91
            if (isset(self::$binaryOperators[$token])) {
3416
                //    We must have two operands, error if we don't
3417 89
                if (($operand2Data = $stack->pop()) === null) {
3418
                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3419
                }
3420 89
                if (($operand1Data = $stack->pop()) === null) {
3421
                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3422
                }
3423
3424 89
                $operand1 = self::dataTestReference($operand1Data);
3425 89
                $operand2 = self::dataTestReference($operand2Data);
3426
3427
                //    Log what we're doing
3428 89
                if ($token == ':') {
3429 32
                    $this->_debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference']));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3430
                } else {
3431 73
                    $this->_debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3432
                }
3433
3434
                //    Process the operation in the appropriate manner
3435
                switch ($token) {
3436
                    //    Comparison (Boolean) Operators
3437 89
                    case '>':            //    Greater than
3438 75
                    case '<':            //    Less than
3439 68
                    case '>=':            //    Greater than or Equal to
3440 60
                    case '<=':            //    Less than or Equal to
3441 53
                    case '=':            //    Equality
3442 44
                    case '<>':            //    Inequality
3443 61
                        $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
3444 61
                        break;
3445
                    //    Binary Operators
3446 37
                    case ':':            //    Range
3447 32
                        $sheet1 = $sheet2 = '';
3448 32
                        if (strpos($operand1Data['reference'], '!') !== false) {
3449 32
                            list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']);
3450
                        } else {
3451
                            $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
3452
                        }
3453 32
                        if (strpos($operand2Data['reference'], '!') !== false) {
3454 32
                            list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']);
3455
                        } else {
3456 9
                            $sheet2 = $sheet1;
3457
                        }
3458 32
                        if ($sheet1 == $sheet2) {
3459 32 View Code Duplication
                            if ($operand1Data['reference'] === null) {
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...
3460
                                if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
3461
                                    $operand1Data['reference'] = $pCell->getColumn() . $operand1Data['value'];
0 ignored issues
show
Bug introduced by
It seems like $pCell is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
3462
                                } elseif (trim($operand1Data['reference']) == '') {
3463
                                    $operand1Data['reference'] = $pCell->getCoordinate();
3464
                                } else {
3465
                                    $operand1Data['reference'] = $operand1Data['value'] . $pCell->getRow();
3466
                                }
3467
                            }
3468 32 View Code Duplication
                            if ($operand2Data['reference'] === null) {
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...
3469
                                if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
3470
                                    $operand2Data['reference'] = $pCell->getColumn() . $operand2Data['value'];
3471
                                } elseif (trim($operand2Data['reference']) == '') {
3472
                                    $operand2Data['reference'] = $pCell->getCoordinate();
3473
                                } else {
3474
                                    $operand2Data['reference'] = $operand2Data['value'] . $pCell->getRow();
3475
                                }
3476
                            }
3477
3478 32
                            $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
3479 32
                            $oCol = $oRow = [];
3480 32
                            foreach ($oData as $oDatum) {
3481 32
                                $oCR = Cell::coordinateFromString($oDatum);
3482 32
                                $oCol[] = Cell::columnIndexFromString($oCR[0]) - 1;
3483 32
                                $oRow[] = $oCR[1];
3484
                            }
3485 32
                            $cellRef = Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . Cell::stringFromColumnIndex(max($oCol)) . max($oRow);
3486 32 View Code Duplication
                            if ($pCellParent !== null) {
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...
3487 32
                                $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false);
3488
                            } else {
3489
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3490
                            }
3491 32
                            $stack->push('Cell Reference', $cellValue, $cellRef);
3492
                        } else {
3493
                            $stack->push('Error', Calculation\Functions::REF(), null);
3494
                        }
3495 32
                        break;
3496 21
                    case '+':            //    Addition
3497 17
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack);
3498 17
                        break;
3499 19
                    case '-':            //    Subtraction
3500 6
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack);
3501 6
                        break;
3502 17
                    case '*':            //    Multiplication
3503 15
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack);
3504 15
                        break;
3505 6
                    case '/':            //    Division
3506 3
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack);
3507 3
                        break;
3508 6
                    case '^':            //    Exponential
3509
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack);
3510
                        break;
3511 6
                    case '&':            //    Concatenation
3512
                        //    If either of the operands is a matrix, we need to treat them both as matrices
3513
                        //        (converting the other operand to a matrix if need be); then perform the required
3514
                        //        matrix operation
3515 6
                        if (is_bool($operand1)) {
3516
                            $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3517
                        }
3518 6
                        if (is_bool($operand2)) {
3519
                            $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3520
                        }
3521 6
                        if ((is_array($operand1)) || (is_array($operand2))) {
3522
                            //    Ensure that both operands are arrays/matrices
3523 5
                            self::checkMatrixOperands($operand1, $operand2, 2);
3524
                            try {
3525
                                //    Convert operand 1 from a PHP array to a matrix
3526 5
                                $matrix = new Shared\JAMA\Matrix($operand1);
3527
                                //    Perform the required operation against the operand 1 matrix, passing in operand 2
3528 5
                                $matrixResult = $matrix->concat($operand2);
3529 5
                                $result = $matrixResult->getArray();
3530
                            } catch (Exception $ex) {
3531
                                $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3532 5
                                $result = '#VALUE!';
3533
                            }
3534
                        } else {
3535 1
                            $result = '"' . str_replace('""', '"', self::unwrapResult($operand1, '"') . self::unwrapResult($operand2, '"')) . '"';
0 ignored issues
show
Unused Code introduced by
The call to Calculation::unwrapResult() has too many arguments starting with '"'.

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

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

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

Loading history...
3536
                        }
3537 6
                        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3538 6
                        $stack->push('Value', $result);
3539 6
                        break;
3540
                    case '|':            //    Intersect
3541
                        $rowIntersect = array_intersect_key($operand1, $operand2);
3542
                        $cellIntersect = $oCol = $oRow = [];
3543
                        foreach (array_keys($rowIntersect) as $row) {
3544
                            $oRow[] = $row;
3545
                            foreach ($rowIntersect[$row] as $col => $data) {
3546
                                $oCol[] = Cell::columnIndexFromString($col) - 1;
3547
                                $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
3548
                            }
3549
                        }
3550
                        $cellRef = Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . Cell::stringFromColumnIndex(max($oCol)) . max($oRow);
3551
                        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3552
                        $stack->push('Value', $cellIntersect, $cellRef);
3553 89
                        break;
3554
                }
3555
3556
            // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
3557 91
            } elseif (($token === '~') || ($token === '%')) {
3558 1
                if (($arg = $stack->pop()) === null) {
3559
                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3560
                }
3561 1
                $arg = $arg['value'];
3562 1
                if ($token === '~') {
3563 1
                    $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3564 1
                    $multiplier = -1;
3565
                } else {
3566
                    $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3567
                    $multiplier = 0.01;
3568
                }
3569 1
                if (is_array($arg)) {
3570
                    self::checkMatrixOperands($arg, $multiplier, 2);
3571
                    try {
3572
                        $matrix1 = new Shared\JAMA\Matrix($arg);
3573
                        $matrixResult = $matrix1->arrayTimesEquals($multiplier);
3574
                        $result = $matrixResult->getArray();
3575
                    } catch (Exception $ex) {
3576
                        $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3577
                        $result = '#VALUE!';
3578
                    }
3579
                    $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3580
                    $stack->push('Value', $result);
3581
                } else {
3582 1
                    $this->executeNumericBinaryOperation($cellID, $multiplier, $arg, '*', 'arrayTimesEquals', $stack);
3583
                }
3584 91
            } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) {
3585 33
                $cellRef = null;
3586 33
                if (isset($matches[8])) {
3587
                    if ($pCell === null) {
3588
                        //                        We can't access the range, so return a REF error
3589
                        $cellValue = Calculation\Functions::REF();
3590
                    } else {
3591
                        $cellRef = $matches[6] . $matches[7] . ':' . $matches[9] . $matches[10];
3592
                        if ($matches[2] > '') {
3593
                            $matches[2] = trim($matches[2], "\"'");
3594 View Code Duplication
                            if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
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...
3595
                                //    It's a Reference to an external spreadsheet (not currently supported)
3596
                                return $this->raiseFormulaError('Unable to access External Workbook');
3597
                            }
3598
                            $matches[2] = trim($matches[2], "\"'");
3599
                            $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3600 View Code Duplication
                            if ($pCellParent !== null) {
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...
3601
                                $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
3602
                            } else {
3603
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3604
                            }
3605
                            $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3606
                        } else {
3607
                            $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3608
                            if ($pCellParent !== null) {
3609
                                $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3610
                            } else {
3611
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3612
                            }
3613
                            $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3614
                        }
3615
                    }
3616
                } else {
3617 33
                    if ($pCell === null) {
3618
                        //                        We can't access the cell, so return a REF error
3619
                        $cellValue = Calculation\Functions::REF();
3620
                    } else {
3621 33
                        $cellRef = $matches[6] . $matches[7];
3622 33
                        if ($matches[2] > '') {
3623 13
                            $matches[2] = trim($matches[2], "\"'");
3624 13 View Code Duplication
                            if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
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...
3625
                                //    It's a Reference to an external spreadsheet (not currently supported)
3626
                                return $this->raiseFormulaError('Unable to access External Workbook');
3627
                            }
3628 13
                            $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3629 13
                            if ($pCellParent !== null) {
3630 13
                                $cellSheet = $this->spreadsheet->getSheetByName($matches[2]);
3631 13
                                if ($cellSheet && $cellSheet->cellExists($cellRef)) {
3632 13
                                    $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
3633 13
                                    $pCell->attach($pCellParent);
3634
                                } else {
3635 13
                                    $cellValue = null;
3636
                                }
3637
                            } else {
3638
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3639
                            }
3640 13
                            $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3641
                        } else {
3642 21
                            $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3643 21
                            if ($pCellParent->isDataSet($cellRef)) {
3644 21
                                $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3645 21
                                $pCell->attach($pCellParent);
0 ignored issues
show
Bug introduced by
It seems like $pCellParent defined by $pCell !== null ? $pCell->getParent() : null on line 3408 can be null; however, PhpOffice\PhpSpreadsheet\Cell::attach() 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...
3646
                            } else {
3647 1
                                $cellValue = null;
3648
                            }
3649 21
                            $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3650
                        }
3651
                    }
3652
                }
3653 33
                $stack->push('Value', $cellValue, $cellRef);
3654
3655
            // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
3656 79
            } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $token, $matches)) {
3657 21
                $functionName = $matches[1];
3658 21
                $argCount = $stack->pop();
3659 21
                $argCount = $argCount['value'];
3660 21
                if ($functionName != 'MKMATRIX') {
3661 21
                    $this->_debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3662
                }
3663 21
                if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) {    // function
3664 21
                    if (isset(self::$phpSpreadsheetFunctions[$functionName])) {
3665 21
                        $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
3666 21
                        $passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']);
3667 21
                        $passCellReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passCellReference']);
3668
                    } elseif (isset(self::$controlFunctions[$functionName])) {
3669
                        $functionCall = self::$controlFunctions[$functionName]['functionCall'];
3670
                        $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
3671
                        $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
3672
                    }
3673
                    // get the arguments for this function
3674 21
                    $args = $argArrayVals = [];
3675 21
                    for ($i = 0; $i < $argCount; ++$i) {
3676 21
                        $arg = $stack->pop();
3677 21
                        $a = $argCount - $i - 1;
3678 21
                        if (($passByReference) &&
0 ignored issues
show
Bug introduced by
The variable $passByReference 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...
3679 21
                            (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) &&
3680 21
                            (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) {
3681
                            if ($arg['reference'] === null) {
3682
                                $args[] = $cellID;
3683
                                if ($functionName != 'MKMATRIX') {
3684
                                    $argArrayVals[] = $this->showValue($cellID);
3685
                                }
3686
                            } else {
3687
                                $args[] = $arg['reference'];
3688
                                if ($functionName != 'MKMATRIX') {
3689
                                    $argArrayVals[] = $this->showValue($arg['reference']);
3690
                                }
3691
                            }
3692
                        } else {
3693 21
                            $args[] = self::unwrapResult($arg['value']);
3694 21
                            if ($functionName != 'MKMATRIX') {
3695 21
                                $argArrayVals[] = $this->showValue($arg['value']);
3696
                            }
3697
                        }
3698
                    }
3699
                    //    Reverse the order of the arguments
3700 21
                    krsort($args);
3701 21
                    if (($passByReference) && ($argCount == 0)) {
3702
                        $args[] = $cellID;
3703
                        $argArrayVals[] = $this->showValue($cellID);
3704
                    }
3705
3706 21
                    if ($functionName != 'MKMATRIX') {
3707 21
                        if ($this->_debugLog->getWriteDebugLog()) {
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3708
                            krsort($argArrayVals);
3709
                            $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator . ' ', Calculation\Functions::flattenArray($argArrayVals)), ' )');
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3710
                        }
3711
                    }
3712
3713
                    //    Process the argument with the appropriate function call
3714 21
                    if ($passCellReference) {
0 ignored issues
show
Bug introduced by
The variable $passCellReference 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...
3715 1
                        $args[] = $pCell;
3716
                    }
3717 21
                    if (strpos($functionCall, '::') !== false) {
3718 21
                        $result = call_user_func_array(explode('::', $functionCall), $args);
0 ignored issues
show
Bug introduced by
The variable $functionCall 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...
3719
                    } else {
3720 1
                        foreach ($args as &$arg) {
3721
                            $arg = Calculation\Functions::flattenSingleValue($arg);
3722
                        }
3723 1
                        unset($arg);
3724 1
                        $result = call_user_func_array($functionCall, $args);
3725
                    }
3726 21
                    if ($functionName != 'MKMATRIX') {
3727 21
                        $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3728
                    }
3729 21
                    $stack->push('Value', self::wrapResult($result));
3730
                }
3731
            } else {
3732
                // if the token is a number, boolean, string or an Excel error, push it onto the stack
3733 79
                if (isset(self::$excelConstants[strtoupper($token)])) {
3734
                    $excelConstant = strtoupper($token);
3735
                    $stack->push('Constant Value', self::$excelConstants[$excelConstant]);
3736
                    $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3737 79
                } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
3738 79
                    $stack->push('Value', $token);
3739
                // if the token is a named range, push the named range name onto the stack
3740 4
                } elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) {
3741 4
                    $namedRange = $matches[6];
3742 4
                    $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3743 4
                    $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false);
3744 4
                    $pCell->attach($pCellParent);
0 ignored issues
show
Bug introduced by
It seems like $pCellParent defined by $pCell !== null ? $pCell->getParent() : null on line 3408 can be null; however, PhpOffice\PhpSpreadsheet\Cell::attach() 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...
3745 4
                    $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3746 4
                    $stack->push('Named Range', $cellValue, $namedRange);
3747
                } else {
3748 91
                    return $this->raiseFormulaError("undefined variable '$token'");
3749
                }
3750
            }
3751
        }
3752
        // when we're out of tokens, the stack should have a single element, the final result
3753 91
        if ($stack->count() != 1) {
3754
            return $this->raiseFormulaError('internal error');
3755
        }
3756 91
        $output = $stack->pop();
3757 91
        $output = $output['value'];
3758
3759
//        if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3760
//            return array_shift(Calculation\Functions::flattenArray($output));
3761
//        }
3762 91
        return $output;
3763
    }
3764
3765 20
    private function validateBinaryOperand($cellID, &$operand, &$stack)
0 ignored issues
show
Unused Code introduced by
The parameter $cellID is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3766
    {
3767 20
        if (is_array($operand)) {
3768 16
            if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
3769
                do {
3770 16
                    $operand = array_pop($operand);
3771 16
                } while (is_array($operand));
3772
            }
3773
        }
3774
        //    Numbers, matrices and booleans can pass straight through, as they're already valid
3775 20
        if (is_string($operand)) {
3776
            //    We only need special validations for the operand if it is a string
3777
            //    Start by stripping off the quotation marks we use to identify true excel string values internally
3778 1
            if ($operand > '' && $operand{0} == '"') {
3779
                $operand = self::unwrapResult($operand);
3780
            }
3781
            //    If the string is a numeric value, we treat it as a numeric, so no further testing
3782 1
            if (!is_numeric($operand)) {
3783
                //    If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
3784 1
                if ($operand > '' && $operand{0} == '#') {
3785
                    $stack->push('Value', $operand);
3786
                    $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3787
3788
                    return false;
3789 1
                } elseif (!Shared\StringHelper::convertToNumberIfFraction($operand)) {
3790
                    //    If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
3791 1
                    $stack->push('Value', '#VALUE!');
3792 1
                    $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3793
3794 1
                    return false;
3795
                }
3796
            }
3797
        }
3798
3799
        //    return a true if the value of the operand is one that we can use in normal binary operations
3800 20
        return true;
3801
    }
3802
3803 61
    private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays = false)
3804
    {
3805
        //    If we're dealing with matrix operations, we want a matrix result
3806 61
        if ((is_array($operand1)) || (is_array($operand2))) {
3807 9
            $result = [];
3808 9
            if ((is_array($operand1)) && (!is_array($operand2))) {
3809 9 View Code Duplication
                foreach ($operand1 as $x => $operandData) {
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...
3810 9
                    $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3811 9
                    $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
3812 9
                    $r = $stack->pop();
3813 9
                    $result[$x] = $r['value'];
3814
                }
3815
            } elseif ((!is_array($operand1)) && (is_array($operand2))) {
3816 View Code Duplication
                foreach ($operand2 as $x => $operandData) {
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...
3817
                    $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3818
                    $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
3819
                    $r = $stack->pop();
3820
                    $result[$x] = $r['value'];
3821
                }
3822
            } else {
3823
                if (!$recursingArrays) {
3824
                    self::checkMatrixOperands($operand1, $operand2, 2);
3825
                }
3826
                foreach ($operand1 as $x => $operandData) {
3827
                    $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3828
                    $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
3829
                    $r = $stack->pop();
3830
                    $result[$x] = $r['value'];
3831
                }
3832
            }
3833
            //    Log the result details
3834 9
            $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3835
            //    And push the result onto the stack
3836 9
            $stack->push('Array', $result);
3837
3838 9
            return true;
3839
        }
3840
3841
        //    Simple validate the two operands if they are string values
3842 61
        if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') {
3843 33
            $operand1 = self::unwrapResult($operand1);
3844
        }
3845 61
        if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') {
3846 42
            $operand2 = self::unwrapResult($operand2);
3847
        }
3848
3849
        // Use case insensitive comparaison if not OpenOffice mode
3850 61
        if (Calculation\Functions::getCompatibilityMode() != Calculation\Functions::COMPATIBILITY_OPENOFFICE) {
3851 61
            if (is_string($operand1)) {
3852 33
                $operand1 = strtoupper($operand1);
3853
            }
3854 61
            if (is_string($operand2)) {
3855 42
                $operand2 = strtoupper($operand2);
3856
            }
3857
        }
3858
3859 61
        $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && Calculation\Functions::getCompatibilityMode() == Calculation\Functions::COMPATIBILITY_OPENOFFICE;
3860
3861
        //    execute the necessary operation
3862
        switch ($operation) {
3863
            //    Greater than
3864 61 View Code Duplication
            case '>':
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...
3865 14
                if ($useLowercaseFirstComparison) {
3866 9
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
3867
                } else {
3868 14
                    $result = ($operand1 > $operand2);
3869
                }
3870 14
                break;
3871
            //    Less than
3872 47 View Code Duplication
            case '<':
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...
3873 7
                if ($useLowercaseFirstComparison) {
3874 4
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
3875
                } else {
3876 7
                    $result = ($operand1 < $operand2);
3877
                }
3878 7
                break;
3879
            //    Equality
3880 40 View Code Duplication
            case '=':
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...
3881 9
                if (is_numeric($operand1) && is_numeric($operand2)) {
3882 3
                    $result = (abs($operand1 - $operand2) < $this->delta);
3883
                } else {
3884 6
                    $result = strcmp($operand1, $operand2) == 0;
3885
                }
3886 9
                break;
3887
            //    Greater than or equal
3888 31
            case '>=':
3889 8
                if (is_numeric($operand1) && is_numeric($operand2)) {
3890 4
                    $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
3891 4
                } elseif ($useLowercaseFirstComparison) {
3892 4
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
3893
                } else {
3894 4
                    $result = strcmp($operand1, $operand2) >= 0;
3895
                }
3896 8
                break;
3897
            //    Less than or equal
3898 23
            case '<=':
3899 7
                if (is_numeric($operand1) && is_numeric($operand2)) {
3900 3
                    $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
3901 4
                } elseif ($useLowercaseFirstComparison) {
3902 4
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
3903
                } else {
3904 4
                    $result = strcmp($operand1, $operand2) <= 0;
3905
                }
3906 7
                break;
3907
            //    Inequality
3908 16 View Code Duplication
            case '<>':
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...
3909 16
                if (is_numeric($operand1) && is_numeric($operand2)) {
3910 3
                    $result = (abs($operand1 - $operand2) > 1E-14);
3911
                } else {
3912 13
                    $result = strcmp($operand1, $operand2) != 0;
3913
                }
3914 16
                break;
3915
        }
3916
3917
        //    Log the result details
3918 61
        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
Bug introduced by
The variable $result 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...
3919
        //    And push the result onto the stack
3920 61
        $stack->push('Value', $result);
3921
3922 61
        return true;
3923
    }
3924
3925
    /**
3926
     * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters
3927
     * @param    string    $str1    First string value for the comparison
3928
     * @param    string    $str2    Second string value for the comparison
3929
     * @return   int
3930
     */
3931 21
    private function strcmpLowercaseFirst($str1, $str2)
3932
    {
3933 21
        $inversedStr1 = Shared\StringHelper::strCaseReverse($str1);
3934 21
        $inversedStr2 = Shared\StringHelper::strCaseReverse($str2);
3935
3936 21
        return strcmp($inversedStr1, $inversedStr2);
3937
    }
3938
3939
    /**
3940
     * @param string $matrixFunction
3941
     */
3942 20
    private function executeNumericBinaryOperation($cellID, $operand1, $operand2, $operation, $matrixFunction, &$stack)
3943
    {
3944
        //    Validate the two operands
3945 20
        if (!$this->validateBinaryOperand($cellID, $operand1, $stack)) {
3946 1
            return false;
3947
        }
3948 20
        if (!$this->validateBinaryOperand($cellID, $operand2, $stack)) {
3949 1
            return false;
3950
        }
3951
3952
        //    If either of the operands is a matrix, we need to treat them both as matrices
3953
        //        (converting the other operand to a matrix if need be); then perform the required
3954
        //        matrix operation
3955 20
        if ((is_array($operand1)) || (is_array($operand2))) {
3956
            //    Ensure that both operands are arrays/matrices of the same size
3957
            self::checkMatrixOperands($operand1, $operand2, 2);
3958
3959
            try {
3960
                //    Convert operand 1 from a PHP array to a matrix
3961
                $matrix = new Shared\JAMA\Matrix($operand1);
3962
                //    Perform the required operation against the operand 1 matrix, passing in operand 2
3963
                $matrixResult = $matrix->$matrixFunction($operand2);
3964
                $result = $matrixResult->getArray();
3965
            } catch (Exception $ex) {
3966
                $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3967
                $result = '#VALUE!';
3968
            }
3969
        } else {
3970 20
            if ((Calculation\Functions::getCompatibilityMode() != Calculation\Functions::COMPATIBILITY_OPENOFFICE) &&
3971 20
                ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1) > 0) ||
3972 20
                 (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0))) {
3973
                $result = Calculation\Functions::VALUE();
3974
            } else {
3975
                //    If we're dealing with non-matrix operations, execute the necessary operation
3976
                switch ($operation) {
3977
                    //    Addition
3978 20
                    case '+':
3979 17
                        $result = $operand1 + $operand2;
3980 17
                        break;
3981
                    //    Subtraction
3982 17
                    case '-':
3983 6
                        $result = $operand1 - $operand2;
3984 6
                        break;
3985
                    //    Multiplication
3986 15
                    case '*':
3987 15
                        $result = $operand1 * $operand2;
3988 15
                        break;
3989
                    //    Division
3990 3
                    case '/':
3991 3
                        if ($operand2 == 0) {
3992
                            //    Trap for Divide by Zero error
3993 3
                            $stack->push('Value', '#DIV/0!');
3994 3
                            $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
3995
3996 3
                            return false;
3997
                        } else {
3998
                            $result = $operand1 / $operand2;
3999
                        }
4000
                        break;
4001
                    //    Power
4002
                    case '^':
4003
                        $result = pow($operand1, $operand2);
4004
                        break;
4005
                }
4006
            }
4007
        }
4008
4009
        //    Log the result details
4010 20
        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
0 ignored issues
show
Bug introduced by
The property _debugLog does not seem to exist. Did you mean debugLog?

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...
Bug introduced by
The variable $result 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...
4011
        //    And push the result onto the stack
4012 20
        $stack->push('Value', $result);
4013
4014 20
        return true;
4015
    }
4016
4017
    // trigger an error, but nicely, if need be
4018
    protected function raiseFormulaError($errorMessage)
4019
    {
4020
        $this->formulaError = $errorMessage;
4021
        $this->cyclicReferenceStack->clear();
0 ignored issues
show
Bug introduced by
The method clear cannot be called on $this->cyclicReferenceStack (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
4022
        if (!$this->suppressFormulaErrors) {
4023
            throw new Calculation\Exception($errorMessage);
4024
        }
4025
        trigger_error($errorMessage, E_USER_ERROR);
4026
    }
4027
4028
    /**
4029
     * Extract range values
4030
     *
4031
     * @param    string      &$pRange    String based range representation
4032
     * @param    Worksheet   $pSheet        Worksheet
4033
     * @param    bool     $resetLog    Flag indicating whether calculation log should be reset or not
4034
     * @throws   Calculation\Exception
4035
     * @return   mixed       Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4036
     */
4037 33
    public function extractCellRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true)
4038
    {
4039
        // Return value
4040 33
        $returnValue = [];
4041
4042 33
        if ($pSheet !== null) {
4043 33
            $pSheetName = $pSheet->getTitle();
4044 33 View Code Duplication
            if (strpos($pRange, '!') !== false) {
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...
4045
                list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
4046
                $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
4047
            }
4048
4049
            // Extract range
4050 33
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
4051 33
            $pRange = $pSheetName . '!' . $pRange;
4052 33
            if (!isset($aReferences[1])) {
4053
                //    Single cell in range
4054 33
                sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
0 ignored issues
show
Bug introduced by
The variable $currentRow 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...
4055 33
                $cellValue = null;
0 ignored issues
show
Unused Code introduced by
$cellValue 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...
4056 33 View Code Duplication
                if ($pSheet->cellExists($aReferences[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...
4057 33
                    $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4058
                } else {
4059 33
                    $returnValue[$currentRow][$currentCol] = null;
4060
                }
4061
            } else {
4062
                // Extract cell data for all cells in the range
4063 32
                foreach ($aReferences as $reference) {
4064
                    // Extract range
4065 32
                    sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
4066 32
                    $cellValue = null;
0 ignored issues
show
Unused Code introduced by
$cellValue 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...
4067 32 View Code Duplication
                    if ($pSheet->cellExists($reference)) {
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...
4068 32
                        $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4069
                    } else {
4070 32
                        $returnValue[$currentRow][$currentCol] = null;
4071
                    }
4072
                }
4073
            }
4074
        }
4075
4076 33
        return $returnValue;
4077
    }
4078
4079
    /**
4080
     * Extract range values
4081
     *
4082
     * @param    string       &$pRange    String based range representation
4083
     * @param    Worksheet    $pSheet        Worksheet
4084
     * @param    bool      $resetLog    Flag indicating whether calculation log should be reset or not
4085
     * @throws   Calculation\Exception
4086
     * @return   mixed        Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4087
     */
4088 4
    public function extractNamedRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true)
4089
    {
4090
        // Return value
4091 4
        $returnValue = [];
4092
4093 4
        if ($pSheet !== null) {
4094 4
            $pSheetName = $pSheet->getTitle();
4095 4 View Code Duplication
            if (strpos($pRange, '!') !== false) {
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...
4096
                list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
4097
                $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
4098
            }
4099
4100
            // Named range?
4101 4
            $namedRange = NamedRange::resolveRange($pRange, $pSheet);
4102 4
            if ($namedRange !== null) {
4103 2
                $pSheet = $namedRange->getWorksheet();
4104 2
                $pRange = $namedRange->getRange();
4105 2
                $splitRange = Cell::splitRange($pRange);
4106
                //    Convert row and column references
4107 2
                if (ctype_alpha($splitRange[0][0])) {
4108
                    $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
4109 2
                } elseif (ctype_digit($splitRange[0][0])) {
4110 2
                    $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
4111
                }
4112
            } else {
4113 2
                return Calculation\Functions::REF();
4114
            }
4115
4116
            // Extract range
4117 2
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
4118 2
            if (!isset($aReferences[1])) {
4119
                //    Single cell (or single column or row) in range
4120 1
                list($currentCol, $currentRow) = Cell::coordinateFromString($aReferences[0]);
4121 1
                $cellValue = null;
0 ignored issues
show
Unused Code introduced by
$cellValue 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...
4122 1 View Code Duplication
                if ($pSheet->cellExists($aReferences[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...
4123 1
                    $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4124
                } else {
4125 1
                    $returnValue[$currentRow][$currentCol] = null;
4126
                }
4127
            } else {
4128
                // Extract cell data for all cells in the range
4129 1
                foreach ($aReferences as $reference) {
4130
                    // Extract range
4131 1
                    list($currentCol, $currentRow) = Cell::coordinateFromString($reference);
4132 1
                    $cellValue = null;
0 ignored issues
show
Unused Code introduced by
$cellValue 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 1 View Code Duplication
                    if ($pSheet->cellExists($reference)) {
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...
4134 1
                        $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4135
                    } else {
4136 1
                        $returnValue[$currentRow][$currentCol] = null;
4137
                    }
4138
                }
4139
            }
4140
        }
4141
4142 2
        return $returnValue;
4143
    }
4144
4145
    /**
4146
     * Is a specific function implemented?
4147
     *
4148
     * @param    string    $pFunction    Function Name
4149
     * @return    bool
4150
     */
4151
    public function isImplemented($pFunction = '')
4152
    {
4153
        $pFunction = strtoupper($pFunction);
4154
        if (isset(self::$phpSpreadsheetFunctions[$pFunction])) {
4155
            return self::$phpSpreadsheetFunctions[$pFunction]['functionCall'] !== '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY';
4156
        } else {
4157
            return false;
4158
        }
4159
    }
4160
4161
    /**
4162
     * Get a list of all implemented functions as an array of function objects
4163
     *
4164
     * @return    array of Calculation\Category
4165
     */
4166
    public function getFunctions()
4167
    {
4168
        return self::$phpSpreadsheetFunctions;
4169
    }
4170
4171
    /**
4172
     * Get a list of implemented Excel function names
4173
     *
4174
     * @return    array
4175
     */
4176 2
    public function getImplementedFunctionNames()
4177
    {
4178 2
        $returnValue = [];
4179 2
        foreach (self::$phpSpreadsheetFunctions as $functionName => $function) {
4180 2
            if ($function['functionCall'] !== '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions::DUMMY') {
4181 2
                $returnValue[] = $functionName;
4182
            }
4183
        }
4184
4185 2
        return $returnValue;
4186
    }
4187
}
4188