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

Calculation::translateFormula()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 30
Code Lines 16

Duplication

Lines 9
Ratio 30 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 3
nop 5
dl 9
loc 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace PhpSpreadsheet;
4
5
if (!defined('CALCULATION_REGEXP_CELLREF')) {
6
    //    Test for support of \P (multibyte options) in PCRE
7
    if (defined('PREG_BAD_UTF8_ERROR')) {
8
        //    Cell reference (cell or range of cells, with or without a sheet reference)
9
        define('CALCULATION_REGEXP_CELLREF', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})');
10
        //    Named Range of cells
11
        define('CALCULATION_REGEXP_NAMEDRANGE', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)');
12
    } else {
13
        //    Cell reference (cell or range of cells, with or without a sheet reference)
14
        define('CALCULATION_REGEXP_CELLREF', '(((\w*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d+)');
15
        //    Named Range of cells
16
        define('CALCULATION_REGEXP_NAMEDRANGE', '(((\w*)|(\'.*\')|(\".*\"))!)?([_A-Z][_A-Z0-9\.]*)');
17
    }
18
}
19
20
/**
21
 * \PhpSpreadsheet\Calculation (Multiton)
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
 * @version    ##VERSION##, ##DATE##
43
 */
44
class Calculation
45
{
46
    /** Constants                */
47
    /** Regular Expressions        */
48
    //    Numeric operand
49
    const CALCULATION_REGEXP_NUMBER        = '[-+]?\d*\.?\d+(e[-+]?\d+)?';
50
    //    String operand
51
    const CALCULATION_REGEXP_STRING        = '"(?:[^"]|"")*"';
52
    //    Opening bracket
53
    const CALCULATION_REGEXP_OPENBRACE    = '\(';
54
    //    Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
55
    const CALCULATION_REGEXP_FUNCTION    = '@?([A-Z][A-Z0-9\.]*)[\s]*\(';
56
    //    Cell reference (cell or range of cells, with or without a sheet reference)
57
    const CALCULATION_REGEXP_CELLREF    = CALCULATION_REGEXP_CELLREF;
58
    //    Named Range of cells
59
    const CALCULATION_REGEXP_NAMEDRANGE    = CALCULATION_REGEXP_NAMEDRANGE;
60
    //    Error
61
    const CALCULATION_REGEXP_ERROR        = '\#[A-Z][A-Z0_\/]*[!\?]?';
62
63
64
    /** constants */
65
    const RETURN_ARRAY_AS_ERROR = 'error';
66
    const RETURN_ARRAY_AS_VALUE = 'value';
67
    const RETURN_ARRAY_AS_ARRAY = 'array';
68
69
    private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
70
71
72
    /**
73
     * Instance of this class
74
     *
75
     * @access    private
76
     * @var \PhpSpreadsheet\Calculation
77
     */
78
    private static $instance;
79
80
81
    /**
82
     * Instance of the spreadsheet this Calculation Engine is using
83
     *
84
     * @access    private
85
     * @var PhpSpreadsheet
86
     */
87
    private $spreadsheet;
88
89
    /**
90
     * List of instances of the calculation engine that we've instantiated for individual spreadsheets
91
     *
92
     * @access    private
93
     * @var \PhpSpreadsheet\Calculation[]
94
     */
95
    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...
96
97
    /**
98
     * Calculation cache
99
     *
100
     * @access    private
101
     * @var array
102
     */
103
    private $calculationCache = array ();
104
105
106
    /**
107
     * Calculation cache enabled
108
     *
109
     * @access    private
110
     * @var boolean
111
     */
112
    private $calculationCacheEnabled = true;
113
114
115
    /**
116
     * List of operators that can be used within formulae
117
     * The true/false value indicates whether it is a binary operator or a unary operator
118
     *
119
     * @access    private
120
     * @var array
121
     */
122
    private static $operators = array(
123
        '+' => true,    '-' => true,    '*' => true,    '/' => true,
124
        '^' => true,    '&' => true,    '%' => false,    '~' => false,
125
        '>' => true,    '<' => true,    '=' => true,    '>=' => true,
126
        '<=' => true,    '<>' => true,    '|' => true,    ':' => true
127
    );
128
129
    /**
130
     * List of binary operators (those that expect two operands)
131
     *
132
     * @access    private
133
     * @var array
134
     */
135
    private static $binaryOperators = array(
136
        '+' => true,    '-' => true,    '*' => true,    '/' => true,
137
        '^' => true,    '&' => true,    '>' => true,    '<' => true,
138
        '=' => true,    '>=' => true,    '<=' => true,    '<>' => true,
139
        '|' => true,    ':' => true
140
    );
141
142
    /**
143
     * The debug log generated by the calculation engine
144
     *
145
     * @access    private
146
     * @var CalcEngine\Logger
147
     *
148
     */
149
    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...
150
151
    /**
152
     * Flag to determine how formula errors should be handled
153
     *        If true, then a user error will be triggered
154
     *        If false, then an exception will be thrown
155
     *
156
     * @access    public
157
     * @var boolean
158
     *
159
     */
160
    public $suppressFormulaErrors = false;
161
162
    /**
163
     * Error message for any error that was raised/thrown by the calculation engine
164
     *
165
     * @access    public
166
     * @var string
167
     *
168
     */
169
    public $formulaError = null;
170
171
    /**
172
     * An array of the nested cell references accessed by the calculation engine, used for the debug log
173
     *
174
     * @access    private
175
     * @var array of string
176
     *
177
     */
178
    private $cyclicReferenceStack;
179
180
    private $cellStack = array();
181
182
    /**
183
     * Current iteration counter for cyclic formulae
184
     * If the value is 0 (or less) then cyclic formulae will throw an exception,
185
     *    otherwise they will iterate to the limit defined here before returning a result
186
     *
187
     * @var integer
188
     *
189
     */
190
    private $cyclicFormulaCounter = 1;
191
192
    private $cyclicFormulaCell = '';
193
194
    /**
195
     * Number of iterations for cyclic formulae
196
     *
197
     * @var integer
198
     *
199
     */
200
    public $cyclicFormulaCount = 1;
201
202
    /**
203
     * Epsilon Precision used for comparisons in calculations
204
     *
205
     * @var float
206
     *
207
     */
208
    private $delta    = 0.1e-12;
209
210
211
    /**
212
     * The current locale setting
213
     *
214
     * @var string
215
     *
216
     */
217
    private static $localeLanguage = 'en_us';                    //    US English    (default locale)
218
219
    /**
220
     * List of available locale settings
221
     * Note that this is read for the locale subdirectory only when requested
222
     *
223
     * @var string[]
224
     *
225
     */
226
    private static $validLocaleLanguages = array(
227
        '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...
228
    );
229
230
    /**
231
     * Locale-specific argument separator for function arguments
232
     *
233
     * @var string
234
     *
235
     */
236
    private static $localeArgumentSeparator = ',';
237
    private static $localeFunctions = array();
238
239
    /**
240
     * Locale-specific translations for Excel constants (True, False and Null)
241
     *
242
     * @var string[]
243
     *
244
     */
245
    public static $localeBoolean = array(
246
        'TRUE'  => 'TRUE',
247
        'FALSE' => 'FALSE',
248
        'NULL'  => 'NULL'
249
    );
250
251
    /**
252
     * Excel constant string translations to their PHP equivalents
253
     * Constant conversion from text name/value to actual (datatyped) value
254
     *
255
     * @var string[]
256
     *
257
     */
258
    private static $excelConstants = array(
259
        'TRUE'  => true,
260
        'FALSE' => false,
261
        'NULL'  => null
262
    );
263
264
     //    PhpSpreadsheet functions
265
    private static $phpSpreadsheetFunctions = array(
266
        'ABS' => array(
267
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
268
            'functionCall' => 'abs',
269
            'argumentCount' => '1'
270
        ),
271
        'ACCRINT' => array(
272
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
273
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::ACCRINT',
274
            'argumentCount' => '4-7'
275
        ),
276
        'ACCRINTM' => array(
277
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
278
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::ACCRINTM',
279
            'argumentCount' => '3-5'
280
        ),
281
        'ACOS' => array(
282
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
283
            'functionCall' => 'acos',
284
            'argumentCount' => '1'
285
        ),
286
        'ACOSH' => array(
287
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
288
            'functionCall' => 'acosh',
289
            'argumentCount' => '1'
290
        ),
291
        'ADDRESS' => array(
292
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
293
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::cellAddress',
294
            'argumentCount' => '2-5'
295
        ),
296
        'AMORDEGRC' => array(
297
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
298
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::AMORDEGRC',
299
            'argumentCount' => '6,7'
300
        ),
301
        'AMORLINC' => array(
302
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
303
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::AMORLINC',
304
            'argumentCount' => '6,7'
305
        ),
306
        'AND' => array(
307
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
308
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::logicalAnd',
309
            'argumentCount' => '1+'
310
        ),
311
        'AREAS' => array(
312
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
313
            'functionCall' => 'Calculation\Categories::DUMMY',
314
            'argumentCount' => '1'
315
        ),
316
        'ASC' => array(
317
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
318
            'functionCall' => 'Calculation\Categories::DUMMY',
319
            'argumentCount' => '1'
320
        ),
321
        'ASIN' => array(
322
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
323
            'functionCall' => 'asin',
324
            'argumentCount' => '1'
325
        ),
326
        'ASINH' => array(
327
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
328
            'functionCall' => 'asinh',
329
            'argumentCount' => '1'
330
        ),
331
        'ATAN' => array(
332
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
333
            'functionCall' => 'atan',
334
            'argumentCount' => '1'
335
        ),
336
        'ATAN2' => array(
337
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
338
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::ATAN2',
339
            'argumentCount' => '2'
340
        ),
341
        'ATANH' => array(
342
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
343
            'functionCall' => 'atanh',
344
            'argumentCount' => '1'
345
        ),
346
        'AVEDEV' => array(
347
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
348
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::AVEDEV',
349
            'argumentCount' => '1+'
350
        ),
351
        'AVERAGE' => array(
352
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
353
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::AVERAGE',
354
            'argumentCount' => '1+'
355
        ),
356
        'AVERAGEA' => array(
357
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
358
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::AVERAGEA',
359
            'argumentCount' => '1+'
360
        ),
361
        'AVERAGEIF' => array(
362
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
363
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::AVERAGEIF',
364
            'argumentCount' => '2,3'
365
        ),
366
        'AVERAGEIFS' => array(
367
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
368
            'functionCall' => 'Calculation\Categories::DUMMY',
369
            'argumentCount' => '3+'
370
        ),
371
        'BAHTTEXT' => array(
372
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
373
            'functionCall' => 'Calculation\Categories::DUMMY',
374
            'argumentCount' => '1'
375
        ),
376
        'BESSELI' => array(
377
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
378
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BESSELI',
379
            'argumentCount' => '2'
380
        ),
381
        'BESSELJ' => array(
382
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
383
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BESSELJ',
384
            'argumentCount' => '2'
385
        ),
386
        'BESSELK' => array(
387
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
388
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BESSELK',
389
            'argumentCount' => '2'
390
        ),
391
        'BESSELY' => array(
392
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
393
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BESSELY',
394
            'argumentCount' => '2'
395
        ),
396
        'BETADIST' => array(
397
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
398
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::BETADIST',
399
            'argumentCount' => '3-5'
400
        ),
401
        'BETAINV' => array(
402
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
403
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::BETAINV',
404
            'argumentCount' => '3-5'
405
        ),
406
        'BIN2DEC' => array(
407
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
408
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BINTODEC',
409
            'argumentCount' => '1'
410
        ),
411
        'BIN2HEX' => array(
412
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
413
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BINTOHEX',
414
            'argumentCount' => '1,2'
415
        ),
416
        'BIN2OCT' => array(
417
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
418
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::BINTOOCT',
419
            'argumentCount' => '1,2'
420
        ),
421
        'BINOMDIST' => array(
422
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
423
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::BINOMDIST',
424
            'argumentCount' => '4'
425
        ),
426
        'CEILING' => array(
427
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
428
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::CEILING',
429
            'argumentCount' => '2'
430
        ),
431
        'CELL' => array(
432
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
433
            'functionCall' => 'Calculation\Categories::DUMMY',
434
            'argumentCount' => '1,2'
435
        ),
436
        'CHAR' => array(
437
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
438
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::CHARACTER',
439
            'argumentCount' => '1'
440
        ),
441
        'CHIDIST' => array(
442
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
443
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::CHIDIST',
444
            'argumentCount' => '2'
445
        ),
446
        'CHIINV' => array(
447
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
448
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::CHIINV',
449
            'argumentCount' => '2'
450
        ),
451
        'CHITEST' => array(
452
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
453
            'functionCall' => 'Calculation\Categories::DUMMY',
454
            'argumentCount' => '2'
455
        ),
456
        'CHOOSE' => array(
457
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
458
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::CHOOSE',
459
            'argumentCount' => '2+'
460
        ),
461
        'CLEAN' => array(
462
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
463
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::TRIMNONPRINTABLE',
464
            'argumentCount' => '1'
465
        ),
466
        'CODE' => array(
467
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
468
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::ASCIICODE',
469
            'argumentCount' => '1'
470
        ),
471
        'COLUMN' => array(
472
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
473
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::COLUMN',
474
            'argumentCount' => '-1',
475
            'passByReference' => array(true)
476
        ),
477
        'COLUMNS' => array(
478
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
479
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::COLUMNS',
480
            'argumentCount' => '1'
481
        ),
482
        'COMBIN' => array(
483
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
484
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::COMBIN',
485
            'argumentCount' => '2'
486
        ),
487
        'COMPLEX' => array(
488
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
489
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::COMPLEX',
490
            'argumentCount' => '2,3'
491
        ),
492
        'CONCATENATE' => array(
493
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
494
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::CONCATENATE',
495
            'argumentCount' => '1+'
496
        ),
497
        'CONFIDENCE' => array(
498
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
499
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::CONFIDENCE',
500
            'argumentCount' => '3'
501
        ),
502
        'CONVERT' => array(
503
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
504
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::CONVERTUOM',
505
            'argumentCount' => '3'
506
        ),
507
        'CORREL' => array(
508
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
509
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::CORREL',
510
            'argumentCount' => '2'
511
        ),
512
        'COS' => array(
513
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
514
            'functionCall' => 'cos',
515
            'argumentCount' => '1'
516
        ),
517
        'COSH' => array(
518
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
519
            'functionCall' => 'cosh',
520
            'argumentCount' => '1'
521
        ),
522
        'COUNT' => array(
523
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
524
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::COUNT',
525
            'argumentCount' => '1+'
526
        ),
527
        'COUNTA' => array(
528
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
529
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::COUNTA',
530
            'argumentCount' => '1+'
531
        ),
532
        'COUNTBLANK' => array(
533
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
534
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::COUNTBLANK',
535
            'argumentCount' => '1'
536
        ),
537
        'COUNTIF' => array(
538
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
539
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::COUNTIF',
540
            'argumentCount' => '2'
541
        ),
542
        'COUNTIFS' => array(
543
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
544
            'functionCall' => 'Calculation\Categories::DUMMY',
545
            'argumentCount' => '2'
546
        ),
547
        'COUPDAYBS' => array(
548
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
549
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::COUPDAYBS',
550
            'argumentCount' => '3,4'
551
        ),
552
        'COUPDAYS' => array(
553
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
554
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::COUPDAYS',
555
            'argumentCount' => '3,4'
556
        ),
557
        'COUPDAYSNC' => array(
558
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
559
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::COUPDAYSNC',
560
            'argumentCount' => '3,4'
561
        ),
562
        'COUPNCD' => array(
563
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
564
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::COUPNCD',
565
            'argumentCount' => '3,4'
566
        ),
567
        'COUPNUM' => array(
568
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
569
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::COUPNUM',
570
            'argumentCount' => '3,4'
571
        ),
572
        'COUPPCD' => array(
573
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
574
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::COUPPCD',
575
            'argumentCount' => '3,4'
576
        ),
577
        'COVAR' => array(
578
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
579
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::COVAR',
580
            'argumentCount' => '2'
581
        ),
582
        'CRITBINOM' => array(
583
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
584
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::CRITBINOM',
585
            'argumentCount' => '3'
586
        ),
587
        'CUBEKPIMEMBER' => array(
588
            'category' => Calculation\Categories::CATEGORY_CUBE,
589
            'functionCall' => 'Calculation\Categories::DUMMY',
590
            'argumentCount' => '?'
591
        ),
592
        'CUBEMEMBER' => array(
593
            'category' => Calculation\Categories::CATEGORY_CUBE,
594
            'functionCall' => 'Calculation\Categories::DUMMY',
595
            'argumentCount' => '?'
596
        ),
597
        'CUBEMEMBERPROPERTY' => array(
598
            'category' => Calculation\Categories::CATEGORY_CUBE,
599
            'functionCall' => 'Calculation\Categories::DUMMY',
600
            'argumentCount' => '?'
601
        ),
602
        'CUBERANKEDMEMBER' => array(
603
            'category' => Calculation\Categories::CATEGORY_CUBE,
604
            'functionCall' => 'Calculation\Categories::DUMMY',
605
            'argumentCount' => '?'
606
        ),
607
        'CUBESET' => array(
608
            'category' => Calculation\Categories::CATEGORY_CUBE,
609
            'functionCall' => 'Calculation\Categories::DUMMY',
610
            'argumentCount' => '?'
611
        ),
612
        'CUBESETCOUNT' => array(
613
            'category' => Calculation\Categories::CATEGORY_CUBE,
614
            'functionCall' => 'Calculation\Categories::DUMMY',
615
            'argumentCount' => '?'
616
        ),
617
        'CUBEVALUE' => array(
618
            'category' => Calculation\Categories::CATEGORY_CUBE,
619
            'functionCall' => 'Calculation\Categories::DUMMY',
620
            'argumentCount' => '?'
621
        ),
622
        'CUMIPMT' => array(
623
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
624
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::CUMIPMT',
625
            'argumentCount' => '6'
626
        ),
627
        'CUMPRINC' => array(
628
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
629
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::CUMPRINC',
630
            'argumentCount' => '6'
631
        ),
632
        'DATE' => array(
633
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
634
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DATE',
635
            'argumentCount' => '3'
636
        ),
637
        'DATEDIF' => array(
638
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
639
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DATEDIF',
640
            'argumentCount' => '2,3'
641
        ),
642
        'DATEVALUE' => array(
643
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
644
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DATEVALUE',
645
            'argumentCount' => '1'
646
        ),
647
        'DAVERAGE' => array(
648
            'category' => Calculation\Categories::CATEGORY_DATABASE,
649
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DAVERAGE',
650
            'argumentCount' => '3'
651
        ),
652
        'DAY' => array(
653
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
654
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DAYOFMONTH',
655
            'argumentCount' => '1'
656
        ),
657
        'DAYS360' => array(
658
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
659
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DAYS360',
660
            'argumentCount' => '2,3'
661
        ),
662
        'DB' => array(
663
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
664
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::DB',
665
            'argumentCount' => '4,5'
666
        ),
667
        'DCOUNT' => array(
668
            'category' => Calculation\Categories::CATEGORY_DATABASE,
669
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DCOUNT',
670
            'argumentCount' => '3'
671
        ),
672
        'DCOUNTA' => array(
673
            'category' => Calculation\Categories::CATEGORY_DATABASE,
674
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DCOUNTA',
675
            'argumentCount' => '3'
676
        ),
677
        'DDB' => array(
678
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
679
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::DDB',
680
            'argumentCount' => '4,5'
681
        ),
682
        'DEC2BIN' => array(
683
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
684
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::DECTOBIN',
685
            'argumentCount' => '1,2'
686
        ),
687
        'DEC2HEX' => array(
688
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
689
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::DECTOHEX',
690
            'argumentCount' => '1,2'
691
        ),
692
        'DEC2OCT' => array(
693
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
694
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::DECTOOCT',
695
            'argumentCount' => '1,2'
696
        ),
697
        'DEGREES' => array(
698
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
699
            'functionCall' => 'rad2deg',
700
            'argumentCount' => '1'
701
        ),
702
        'DELTA' => array(
703
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
704
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::DELTA',
705
            'argumentCount' => '1,2'
706
        ),
707
        'DEVSQ' => array(
708
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
709
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::DEVSQ',
710
            'argumentCount' => '1+'
711
        ),
712
        'DGET' => array(
713
            'category' => Calculation\Categories::CATEGORY_DATABASE,
714
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DGET',
715
            'argumentCount' => '3'
716
        ),
717
        'DISC' => array(
718
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
719
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::DISC',
720
            'argumentCount' => '4,5'
721
        ),
722
        'DMAX' => array(
723
            'category' => Calculation\Categories::CATEGORY_DATABASE,
724
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DMAX',
725
            'argumentCount' => '3'
726
        ),
727
        'DMIN' => array(
728
            'category' => Calculation\Categories::CATEGORY_DATABASE,
729
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DMIN',
730
            'argumentCount' => '3'
731
        ),
732
        'DOLLAR' => array(
733
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
734
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::DOLLAR',
735
            'argumentCount' => '1,2'
736
        ),
737
        'DOLLARDE' => array(
738
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
739
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::DOLLARDE',
740
            'argumentCount' => '2'
741
        ),
742
        'DOLLARFR' => array(
743
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
744
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::DOLLARFR',
745
            'argumentCount' => '2'
746
        ),
747
        'DPRODUCT' => array(
748
            'category' => Calculation\Categories::CATEGORY_DATABASE,
749
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DPRODUCT',
750
            'argumentCount' => '3'
751
        ),
752
        'DSTDEV' => array(
753
            'category' => Calculation\Categories::CATEGORY_DATABASE,
754
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DSTDEV',
755
            'argumentCount' => '3'
756
        ),
757
        'DSTDEVP' => array(
758
            'category' => Calculation\Categories::CATEGORY_DATABASE,
759
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DSTDEVP',
760
            'argumentCount' => '3'
761
        ),
762
        'DSUM' => array(
763
            'category' => Calculation\Categories::CATEGORY_DATABASE,
764
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DSUM',
765
            'argumentCount' => '3'
766
        ),
767
        'DURATION' => array(
768
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
769
            'functionCall' => 'Calculation\Categories::DUMMY',
770
            'argumentCount' => '5,6'
771
        ),
772
        'DVAR' => array(
773
            'category' => Calculation\Categories::CATEGORY_DATABASE,
774
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DVAR',
775
            'argumentCount' => '3'
776
        ),
777
        'DVARP' => array(
778
            'category' => Calculation\Categories::CATEGORY_DATABASE,
779
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Database::DVARP',
780
            'argumentCount' => '3'
781
        ),
782
        'EDATE' => array(
783
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
784
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::EDATE',
785
            'argumentCount' => '2'
786
        ),
787
        'EFFECT' => array(
788
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
789
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::EFFECT',
790
            'argumentCount' => '2'
791
        ),
792
        'EOMONTH' => array(
793
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
794
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::EOMONTH',
795
            'argumentCount' => '2'
796
        ),
797
        'ERF' => array(
798
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
799
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::ERF',
800
            'argumentCount' => '1,2'
801
        ),
802
        'ERFC' => array(
803
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
804
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::ERFC',
805
            'argumentCount' => '1'
806
        ),
807
        'ERROR.TYPE' => array(
808
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
809
            'functionCall' => 'Calculation\Categories::errorType',
810
            'argumentCount' => '1'
811
        ),
812
        'EVEN' => array(
813
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
814
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::EVEN',
815
            'argumentCount' => '1'
816
        ),
817
        'EXACT' => array(
818
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
819
            'functionCall' => 'Calculation\Categories::DUMMY',
820
            'argumentCount' => '2'
821
        ),
822
        'EXP' => array(
823
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
824
            'functionCall' => 'exp',
825
            'argumentCount' => '1'
826
        ),
827
        'EXPONDIST' => array(
828
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
829
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::EXPONDIST',
830
            'argumentCount' => '3'
831
        ),
832
        'FACT' => array(
833
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
834
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::FACT',
835
            'argumentCount' => '1'
836
        ),
837
        'FACTDOUBLE' => array(
838
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
839
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::FACTDOUBLE',
840
            'argumentCount' => '1'
841
        ),
842
        'FALSE' => array(
843
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
844
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::FALSE',
845
            'argumentCount' => '0'
846
        ),
847
        'FDIST' => array(
848
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
849
            'functionCall' => 'Calculation\Categories::DUMMY',
850
            'argumentCount' => '3'
851
        ),
852
        'FIND' => array(
853
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
854
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::SEARCHSENSITIVE',
855
            'argumentCount' => '2,3'
856
        ),
857
        'FINDB' => array(
858
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
859
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::SEARCHSENSITIVE',
860
            'argumentCount' => '2,3'
861
        ),
862
        'FINV' => array(
863
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
864
            'functionCall' => 'Calculation\Categories::DUMMY',
865
            'argumentCount' => '3'
866
        ),
867
        'FISHER' => array(
868
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
869
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::FISHER',
870
            'argumentCount' => '1'
871
        ),
872
        'FISHERINV' => array(
873
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
874
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::FISHERINV',
875
            'argumentCount' => '1'
876
        ),
877
        'FIXED' => array(
878
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
879
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::FIXEDFORMAT',
880
            'argumentCount' => '1-3'
881
        ),
882
        'FLOOR' => array(
883
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
884
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::FLOOR',
885
            'argumentCount' => '2'
886
        ),
887
        'FORECAST' => array(
888
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
889
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::FORECAST',
890
            'argumentCount' => '3'
891
        ),
892
        'FREQUENCY' => array(
893
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
894
            'functionCall' => 'Calculation\Categories::DUMMY',
895
            'argumentCount' => '2'
896
        ),
897
        'FTEST' => array(
898
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
899
            'functionCall' => 'Calculation\Categories::DUMMY',
900
            'argumentCount' => '2'
901
        ),
902
        'FV' => array(
903
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
904
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::FV',
905
            'argumentCount' => '3-5'
906
        ),
907
        'FVSCHEDULE' => array(
908
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
909
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::FVSCHEDULE',
910
            'argumentCount' => '2'
911
        ),
912
        'GAMMADIST' => array(
913
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
914
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::GAMMADIST',
915
            'argumentCount' => '4'
916
        ),
917
        'GAMMAINV' => array(
918
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
919
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::GAMMAINV',
920
            'argumentCount' => '3'
921
        ),
922
        'GAMMALN' => array(
923
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
924
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::GAMMALN',
925
            'argumentCount' => '1'
926
        ),
927
        'GCD' => array(
928
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
929
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::GCD',
930
            'argumentCount' => '1+'
931
        ),
932
        'GEOMEAN' => array(
933
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
934
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::GEOMEAN',
935
            'argumentCount' => '1+'
936
        ),
937
        'GESTEP' => array(
938
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
939
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::GESTEP',
940
            'argumentCount' => '1,2'
941
        ),
942
        'GETPIVOTDATA' => array(
943
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
944
            'functionCall' => 'Calculation\Categories::DUMMY',
945
            'argumentCount' => '2+'
946
        ),
947
        'GROWTH' => array(
948
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
949
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::GROWTH',
950
            'argumentCount' => '1-4'
951
        ),
952
        'HARMEAN' => array(
953
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
954
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::HARMEAN',
955
            'argumentCount' => '1+'
956
        ),
957
        'HEX2BIN' => array(
958
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
959
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::HEXTOBIN',
960
            'argumentCount' => '1,2'
961
        ),
962
        'HEX2DEC' => array(
963
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
964
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::HEXTODEC',
965
            'argumentCount' => '1'
966
        ),
967
        'HEX2OCT' => array(
968
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
969
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::HEXTOOCT',
970
            'argumentCount' => '1,2'
971
        ),
972
        'HLOOKUP' => array(
973
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
974
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::HLOOKUP',
975
            'argumentCount' => '3,4'
976
        ),
977
        'HOUR' => array(
978
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
979
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::HOUROFDAY',
980
            'argumentCount' => '1'
981
        ),
982
        'HYPERLINK' => array(
983
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
984
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::HYPERLINK',
985
            'argumentCount' => '1,2',
986
            'passCellReference' => true
987
        ),
988
        'HYPGEOMDIST' => array(
989
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
990
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::HYPGEOMDIST',
991
            'argumentCount' => '4'
992
        ),
993
        'IF' => array(
994
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
995
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::statementIf',
996
            'argumentCount' => '1-3'
997
        ),
998
        'IFERROR' => array(
999
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
1000
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::IFERROR',
1001
            'argumentCount' => '2'
1002
        ),
1003
        'IMABS' => array(
1004
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1005
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMABS',
1006
            'argumentCount' => '1'
1007
        ),
1008
        'IMAGINARY' => array(
1009
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1010
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMAGINARY',
1011
            'argumentCount' => '1'
1012
        ),
1013
        'IMARGUMENT' => array(
1014
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1015
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMARGUMENT',
1016
            'argumentCount' => '1'
1017
        ),
1018
        'IMCONJUGATE' => array(
1019
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1020
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMCONJUGATE',
1021
            'argumentCount' => '1'
1022
        ),
1023
        'IMCOS' => array(
1024
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1025
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMCOS',
1026
            'argumentCount' => '1'
1027
        ),
1028
        'IMDIV' => array(
1029
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1030
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMDIV',
1031
            'argumentCount' => '2'
1032
        ),
1033
        'IMEXP' => array(
1034
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1035
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMEXP',
1036
            'argumentCount' => '1'
1037
        ),
1038
        'IMLN' => array(
1039
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1040
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMLN',
1041
            'argumentCount' => '1'
1042
        ),
1043
        'IMLOG10' => array(
1044
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1045
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMLOG10',
1046
            'argumentCount' => '1'
1047
        ),
1048
        'IMLOG2' => array(
1049
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1050
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMLOG2',
1051
            'argumentCount' => '1'
1052
        ),
1053
        'IMPOWER' => array(
1054
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1055
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMPOWER',
1056
            'argumentCount' => '2'
1057
        ),
1058
        'IMPRODUCT' => array(
1059
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1060
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMPRODUCT',
1061
            'argumentCount' => '1+'
1062
        ),
1063
        'IMREAL' => array(
1064
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1065
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMREAL',
1066
            'argumentCount' => '1'
1067
        ),
1068
        'IMSIN' => array(
1069
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1070
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMSIN',
1071
            'argumentCount' => '1'
1072
        ),
1073
        'IMSQRT' => array(
1074
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1075
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMSQRT',
1076
            'argumentCount' => '1'
1077
        ),
1078
        'IMSUB' => array(
1079
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1080
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMSUB',
1081
            'argumentCount' => '2'
1082
        ),
1083
        'IMSUM' => array(
1084
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1085
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::IMSUM',
1086
            'argumentCount' => '1+'
1087
        ),
1088
        'INDEX' => array(
1089
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1090
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::INDEX',
1091
            'argumentCount' => '1-4'
1092
        ),
1093
        'INDIRECT' => array(
1094
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1095
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::INDIRECT',
1096
            'argumentCount' => '1,2',
1097
            'passCellReference' => true
1098
        ),
1099
        'INFO' => array(
1100
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1101
            'functionCall' => 'Calculation\Categories::DUMMY',
1102
            'argumentCount' => '1'
1103
        ),
1104
        'INT' => array(
1105
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1106
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::INT',
1107
            'argumentCount' => '1'
1108
        ),
1109
        'INTERCEPT' => array(
1110
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1111
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::INTERCEPT',
1112
            'argumentCount' => '2'
1113
        ),
1114
        'INTRATE' => array(
1115
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1116
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::INTRATE',
1117
            'argumentCount' => '4,5'
1118
        ),
1119
        'IPMT' => array(
1120
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1121
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::IPMT',
1122
            'argumentCount' => '4-6'
1123
        ),
1124
        'IRR' => array(
1125
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1126
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::IRR',
1127
            'argumentCount' => '1,2'
1128
        ),
1129
        'ISBLANK' => array(
1130
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1131
            'functionCall' => 'Calculation\Categories::isBlank',
1132
            'argumentCount' => '1'
1133
        ),
1134
        'ISERR' => array(
1135
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1136
            'functionCall' => 'Calculation\Categories::IS_ERR',
1137
            'argumentCount' => '1'
1138
        ),
1139
        'ISERROR' => array(
1140
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1141
            'functionCall' => 'Calculation\Categories::IS_ERROR',
1142
            'argumentCount' => '1'
1143
        ),
1144
        'ISEVEN' => array(
1145
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1146
            'functionCall' => 'Calculation\Categories::isEven',
1147
            'argumentCount' => '1'
1148
        ),
1149
        'ISLOGICAL' => array(
1150
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1151
            'functionCall' => 'Calculation\Categories::isLogical',
1152
            'argumentCount' => '1'
1153
        ),
1154
        'ISNA' => array(
1155
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1156
            'functionCall' => 'Calculation\Categories::isNa',
1157
            'argumentCount' => '1'
1158
        ),
1159
        'ISNONTEXT' => array(
1160
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1161
            'functionCall' => 'Calculation\Categories::isNonText',
1162
            'argumentCount' => '1'
1163
        ),
1164
        'ISNUMBER' => array(
1165
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1166
            'functionCall' => 'Calculation\Categories::isNumber',
1167
            'argumentCount' => '1'
1168
        ),
1169
        'ISODD' => array(
1170
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1171
            'functionCall' => 'Calculation\Categories::isOdd',
1172
            'argumentCount' => '1'
1173
        ),
1174
        'ISPMT' => array(
1175
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1176
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::ISPMT',
1177
            'argumentCount' => '4'
1178
        ),
1179
        'ISREF' => array(
1180
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1181
            'functionCall' => 'Calculation\Categories::DUMMY',
1182
            'argumentCount' => '1'
1183
        ),
1184
        'ISTEXT' => array(
1185
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1186
            'functionCall' => 'Calculation\Categories::isText',
1187
            'argumentCount' => '1'
1188
        ),
1189
        'JIS' => array(
1190
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1191
            'functionCall' => 'Calculation\Categories::DUMMY',
1192
            'argumentCount' => '1'
1193
        ),
1194
        'KURT' => array(
1195
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1196
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::KURT',
1197
            'argumentCount' => '1+'
1198
        ),
1199
        'LARGE' => array(
1200
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1201
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::LARGE',
1202
            'argumentCount' => '2'
1203
        ),
1204
        'LCM' => array(
1205
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1206
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::LCM',
1207
            'argumentCount' => '1+'
1208
        ),
1209
        'LEFT' => array(
1210
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1211
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::LEFT',
1212
            'argumentCount' => '1,2'
1213
        ),
1214
        'LEFTB' => array(
1215
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1216
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::LEFT',
1217
            'argumentCount' => '1,2'
1218
        ),
1219
        'LEN' => array(
1220
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1221
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::STRINGLENGTH',
1222
            'argumentCount' => '1'
1223
        ),
1224
        'LENB' => array(
1225
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1226
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::STRINGLENGTH',
1227
            'argumentCount' => '1'
1228
        ),
1229
        'LINEST' => array(
1230
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1231
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::LINEST',
1232
            'argumentCount' => '1-4'
1233
        ),
1234
        'LN' => array(
1235
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1236
            'functionCall' => 'log',
1237
            'argumentCount' => '1'
1238
        ),
1239
        'LOG' => array(
1240
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1241
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::logBase',
1242
            'argumentCount' => '1,2'
1243
        ),
1244
        'LOG10' => array(
1245
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1246
            'functionCall' => 'log10',
1247
            'argumentCount' => '1'
1248
        ),
1249
        'LOGEST' => array(
1250
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1251
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::LOGEST',
1252
            'argumentCount' => '1-4'
1253
        ),
1254
        'LOGINV' => array(
1255
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1256
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::LOGINV',
1257
            'argumentCount' => '3'
1258
        ),
1259
        'LOGNORMDIST' => array(
1260
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1261
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::LOGNORMDIST',
1262
            'argumentCount' => '3'
1263
        ),
1264
        'LOOKUP' => array(
1265
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1266
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::LOOKUP',
1267
            'argumentCount' => '2,3'
1268
        ),
1269
        'LOWER' => array(
1270
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1271
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::LOWERCASE',
1272
            'argumentCount' => '1'
1273
        ),
1274
        'MATCH' => array(
1275
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1276
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::MATCH',
1277
            'argumentCount' => '2,3'
1278
        ),
1279
        'MAX' => array(
1280
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1281
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MAX',
1282
            'argumentCount' => '1+'
1283
        ),
1284
        'MAXA' => array(
1285
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1286
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MAXA',
1287
            'argumentCount' => '1+'
1288
        ),
1289
        'MAXIF' => array(
1290
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1291
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MAXIF',
1292
            'argumentCount' => '2+'
1293
        ),
1294
        'MDETERM' => array(
1295
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1296
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::MDETERM',
1297
            'argumentCount' => '1'
1298
        ),
1299
        'MDURATION' => array(
1300
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1301
            'functionCall' => 'Calculation\Categories::DUMMY',
1302
            'argumentCount' => '5,6'
1303
        ),
1304
        'MEDIAN' => array(
1305
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1306
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MEDIAN',
1307
            'argumentCount' => '1+'
1308
        ),
1309
        'MEDIANIF' => array(
1310
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1311
            'functionCall' => 'Calculation\Categories::DUMMY',
1312
            'argumentCount' => '2+'
1313
        ),
1314
        'MID' => array(
1315
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1316
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::MID',
1317
            'argumentCount' => '3'
1318
        ),
1319
        'MIDB' => array(
1320
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1321
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::MID',
1322
            'argumentCount' => '3'
1323
        ),
1324
        'MIN' => array(
1325
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1326
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MIN',
1327
            'argumentCount' => '1+'
1328
        ),
1329
        'MINA' => array(
1330
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1331
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MINA',
1332
            'argumentCount' => '1+'
1333
        ),
1334
        'MINIF' => array(
1335
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1336
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MINIF',
1337
            'argumentCount' => '2+'
1338
        ),
1339
        'MINUTE' => array(
1340
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1341
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::MINUTEOFHOUR',
1342
            'argumentCount' => '1'
1343
        ),
1344
        'MINVERSE' => array(
1345
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1346
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::MINVERSE',
1347
            'argumentCount' => '1'
1348
        ),
1349
        'MIRR' => array(
1350
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1351
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::MIRR',
1352
            'argumentCount' => '3'
1353
        ),
1354
        'MMULT' => array(
1355
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1356
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::MMULT',
1357
            'argumentCount' => '2'
1358
        ),
1359
        'MOD' => array(
1360
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1361
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::MOD',
1362
            'argumentCount' => '2'
1363
        ),
1364
        'MODE' => array(
1365
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1366
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::MODE',
1367
            'argumentCount' => '1+'
1368
        ),
1369
        'MONTH' => array(
1370
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1371
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::MONTHOFYEAR',
1372
            'argumentCount' => '1'
1373
        ),
1374
        'MROUND' => array(
1375
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1376
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::MROUND',
1377
            'argumentCount' => '2'
1378
        ),
1379
        'MULTINOMIAL' => array(
1380
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1381
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::MULTINOMIAL',
1382
            'argumentCount' => '1+'
1383
        ),
1384
        'N' => array(
1385
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1386
            'functionCall' => 'Calculation\Categories::N',
1387
            'argumentCount' => '1'
1388
        ),
1389
        'NA' => array(
1390
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1391
            'functionCall' => 'Calculation\Categories::NA',
1392
            'argumentCount' => '0'
1393
        ),
1394
        'NEGBINOMDIST' => array(
1395
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1396
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::NEGBINOMDIST',
1397
            'argumentCount' => '3'
1398
        ),
1399
        'NETWORKDAYS' => array(
1400
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1401
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::NETWORKDAYS',
1402
            'argumentCount' => '2+'
1403
        ),
1404
        'NOMINAL' => array(
1405
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1406
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::NOMINAL',
1407
            'argumentCount' => '2'
1408
        ),
1409
        'NORMDIST' => array(
1410
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1411
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::NORMDIST',
1412
            'argumentCount' => '4'
1413
        ),
1414
        'NORMINV' => array(
1415
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1416
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::NORMINV',
1417
            'argumentCount' => '3'
1418
        ),
1419
        'NORMSDIST' => array(
1420
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1421
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::NORMSDIST',
1422
            'argumentCount' => '1'
1423
        ),
1424
        'NORMSINV' => array(
1425
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1426
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::NORMSINV',
1427
            'argumentCount' => '1'
1428
        ),
1429
        'NOT' => array(
1430
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
1431
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::NOT',
1432
            'argumentCount' => '1'
1433
        ),
1434
        'NOW' => array(
1435
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1436
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DATETIMENOW',
1437
            'argumentCount' => '0'
1438
        ),
1439
        'NPER' => array(
1440
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1441
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::NPER',
1442
            'argumentCount' => '3-5'
1443
        ),
1444
        'NPV' => array(
1445
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1446
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::NPV',
1447
            'argumentCount' => '2+'
1448
        ),
1449
        'OCT2BIN' => array(
1450
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1451
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::OCTTOBIN',
1452
            'argumentCount' => '1,2'
1453
        ),
1454
        'OCT2DEC' => array(
1455
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1456
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::OCTTODEC',
1457
            'argumentCount' => '1'
1458
        ),
1459
        'OCT2HEX' => array(
1460
            'category' => Calculation\Categories::CATEGORY_ENGINEERING,
1461
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Engineering::OCTTOHEX',
1462
            'argumentCount' => '1,2'
1463
        ),
1464
        'ODD' => array(
1465
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1466
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::ODD',
1467
            'argumentCount' => '1'
1468
        ),
1469
        'ODDFPRICE' => array(
1470
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1471
            'functionCall' => 'Calculation\Categories::DUMMY',
1472
            'argumentCount' => '8,9'
1473
        ),
1474
        'ODDFYIELD' => array(
1475
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1476
            'functionCall' => 'Calculation\Categories::DUMMY',
1477
            'argumentCount' => '8,9'
1478
        ),
1479
        'ODDLPRICE' => array(
1480
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1481
            'functionCall' => 'Calculation\Categories::DUMMY',
1482
            'argumentCount' => '7,8'
1483
        ),
1484
        'ODDLYIELD' => array(
1485
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1486
            'functionCall' => 'Calculation\Categories::DUMMY',
1487
            'argumentCount' => '7,8'
1488
        ),
1489
        'OFFSET' => array(
1490
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1491
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::OFFSET',
1492
            'argumentCount' => '3-5',
1493
            'passCellReference' => true,
1494
            'passByReference' => array(true)
1495
        ),
1496
        'OR' => array(
1497
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
1498
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::logicalOr',
1499
            'argumentCount' => '1+'
1500
        ),
1501
        'PEARSON' => array(
1502
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1503
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::CORREL',
1504
            'argumentCount' => '2'
1505
        ),
1506
        'PERCENTILE' => array(
1507
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1508
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::PERCENTILE',
1509
            'argumentCount' => '2'
1510
        ),
1511
        'PERCENTRANK' => array(
1512
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1513
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::PERCENTRANK',
1514
            'argumentCount' => '2,3'
1515
        ),
1516
        'PERMUT' => array(
1517
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1518
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::PERMUT',
1519
            'argumentCount' => '2'
1520
        ),
1521
        'PHONETIC' => array(
1522
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1523
            'functionCall' => 'Calculation\Categories::DUMMY',
1524
            'argumentCount' => '1'
1525
        ),
1526
        'PI' => array(
1527
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1528
            'functionCall' => 'pi',
1529
            'argumentCount' => '0'
1530
        ),
1531
        'PMT' => array(
1532
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1533
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::PMT',
1534
            'argumentCount' => '3-5'
1535
        ),
1536
        'POISSON' => array(
1537
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1538
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::POISSON',
1539
            'argumentCount' => '3'
1540
        ),
1541
        'POWER' => array(
1542
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1543
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::POWER',
1544
            'argumentCount' => '2'
1545
        ),
1546
        'PPMT' => array(
1547
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1548
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::PPMT',
1549
            'argumentCount' => '4-6'
1550
        ),
1551
        'PRICE' => array(
1552
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1553
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::PRICE',
1554
            'argumentCount' => '6,7'
1555
        ),
1556
        'PRICEDISC' => array(
1557
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1558
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::PRICEDISC',
1559
            'argumentCount' => '4,5'
1560
        ),
1561
        'PRICEMAT' => array(
1562
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1563
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::PRICEMAT',
1564
            'argumentCount' => '5,6'
1565
        ),
1566
        'PROB' => array(
1567
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1568
            'functionCall' => 'Calculation\Categories::DUMMY',
1569
            'argumentCount' => '3,4'
1570
        ),
1571
        'PRODUCT' => array(
1572
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1573
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::PRODUCT',
1574
            'argumentCount' => '1+'
1575
        ),
1576
        'PROPER' => array(
1577
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1578
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::PROPERCASE',
1579
            'argumentCount' => '1'
1580
        ),
1581
        'PV' => array(
1582
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1583
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::PV',
1584
            'argumentCount' => '3-5'
1585
        ),
1586
        'QUARTILE' => array(
1587
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1588
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::QUARTILE',
1589
            'argumentCount' => '2'
1590
        ),
1591
        'QUOTIENT' => array(
1592
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1593
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::QUOTIENT',
1594
            'argumentCount' => '2'
1595
        ),
1596
        'RADIANS' => array(
1597
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1598
            'functionCall' => 'deg2rad',
1599
            'argumentCount' => '1'
1600
        ),
1601
        'RAND' => array(
1602
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1603
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::RAND',
1604
            'argumentCount' => '0'
1605
        ),
1606
        'RANDBETWEEN' => array(
1607
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1608
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::RAND',
1609
            'argumentCount' => '2'
1610
        ),
1611
        'RANK' => array(
1612
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1613
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::RANK',
1614
            'argumentCount' => '2,3'
1615
        ),
1616
        'RATE' => array(
1617
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1618
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::RATE',
1619
            'argumentCount' => '3-6'
1620
        ),
1621
        'RECEIVED' => array(
1622
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1623
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::RECEIVED',
1624
            'argumentCount' => '4-5'
1625
        ),
1626
        'REPLACE' => array(
1627
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1628
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::REPLACE',
1629
            'argumentCount' => '4'
1630
        ),
1631
        'REPLACEB' => array(
1632
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1633
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::REPLACE',
1634
            'argumentCount' => '4'
1635
        ),
1636
        'REPT' => array(
1637
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1638
            'functionCall' => 'str_repeat',
1639
            'argumentCount' => '2'
1640
        ),
1641
        'RIGHT' => array(
1642
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1643
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::RIGHT',
1644
            'argumentCount' => '1,2'
1645
        ),
1646
        'RIGHTB' => array(
1647
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1648
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::RIGHT',
1649
            'argumentCount' => '1,2'
1650
        ),
1651
        'ROMAN' => array(
1652
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1653
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::ROMAN',
1654
            'argumentCount' => '1,2'
1655
        ),
1656
        'ROUND' => array(
1657
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1658
            'functionCall' => 'round',
1659
            'argumentCount' => '2'
1660
        ),
1661
        'ROUNDDOWN' => array(
1662
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1663
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::ROUNDDOWN',
1664
            'argumentCount' => '2'
1665
        ),
1666
        'ROUNDUP' => array(
1667
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1668
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::ROUNDUP',
1669
            'argumentCount' => '2'
1670
        ),
1671
        'ROW' => array(
1672
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1673
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::ROW',
1674
            'argumentCount' => '-1',
1675
            'passByReference' => array(true)
1676
        ),
1677
        'ROWS' => array(
1678
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1679
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::ROWS',
1680
            'argumentCount' => '1'
1681
        ),
1682
        'RSQ' => array(
1683
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1684
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::RSQ',
1685
            'argumentCount' => '2'
1686
        ),
1687
        'RTD' => array(
1688
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1689
            'functionCall' => 'Calculation\Categories::DUMMY',
1690
            'argumentCount' => '1+'
1691
        ),
1692
        'SEARCH' => array(
1693
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1694
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::SEARCHINSENSITIVE',
1695
            'argumentCount' => '2,3'
1696
        ),
1697
        'SEARCHB' => array(
1698
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1699
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::SEARCHINSENSITIVE',
1700
            'argumentCount' => '2,3'
1701
        ),
1702
        'SECOND' => array(
1703
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1704
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::SECONDOFMINUTE',
1705
            'argumentCount' => '1'
1706
        ),
1707
        'SERIESSUM' => array(
1708
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1709
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SERIESSUM',
1710
            'argumentCount' => '4'
1711
        ),
1712
        'SIGN' => array(
1713
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1714
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SIGN',
1715
            'argumentCount' => '1'
1716
        ),
1717
        'SIN' => array(
1718
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1719
            'functionCall' => 'sin',
1720
            'argumentCount' => '1'
1721
        ),
1722
        'SINH' => array(
1723
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1724
            'functionCall' => 'sinh',
1725
            'argumentCount' => '1'
1726
        ),
1727
        'SKEW' => array(
1728
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1729
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::SKEW',
1730
            'argumentCount' => '1+'
1731
        ),
1732
        'SLN' => array(
1733
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1734
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::SLN',
1735
            'argumentCount' => '3'
1736
        ),
1737
        'SLOPE' => array(
1738
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1739
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::SLOPE',
1740
            'argumentCount' => '2'
1741
        ),
1742
        'SMALL' => array(
1743
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1744
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::SMALL',
1745
            'argumentCount' => '2'
1746
        ),
1747
        'SQRT' => array(
1748
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1749
            'functionCall' => 'sqrt',
1750
            'argumentCount' => '1'
1751
        ),
1752
        'SQRTPI' => array(
1753
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1754
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SQRTPI',
1755
            'argumentCount' => '1'
1756
        ),
1757
        'STANDARDIZE' => array(
1758
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1759
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::STANDARDIZE',
1760
            'argumentCount' => '3'
1761
        ),
1762
        'STDEV' => array(
1763
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1764
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::STDEV',
1765
            'argumentCount' => '1+'
1766
        ),
1767
        'STDEVA' => array(
1768
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1769
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::STDEVA',
1770
            'argumentCount' => '1+'
1771
        ),
1772
        'STDEVP' => array(
1773
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1774
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::STDEVP',
1775
            'argumentCount' => '1+'
1776
        ),
1777
        'STDEVPA' => array(
1778
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1779
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::STDEVPA',
1780
            'argumentCount' => '1+'
1781
        ),
1782
        'STEYX' => array(
1783
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1784
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::STEYX',
1785
            'argumentCount' => '2'
1786
        ),
1787
        'SUBSTITUTE' => array(
1788
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1789
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::SUBSTITUTE',
1790
            'argumentCount' => '3,4'
1791
        ),
1792
        'SUBTOTAL' => array(
1793
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1794
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUBTOTAL',
1795
            'argumentCount' => '2+'
1796
        ),
1797
        'SUM' => array(
1798
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1799
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUM',
1800
            'argumentCount' => '1+'
1801
        ),
1802
        'SUMIF' => array(
1803
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1804
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMIF',
1805
            'argumentCount' => '2,3'
1806
        ),
1807
        'SUMIFS' => array(
1808
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1809
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMIFS',
1810
            'argumentCount' => '3+'
1811
        ),
1812
        'SUMPRODUCT' => array(
1813
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1814
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMPRODUCT',
1815
            'argumentCount' => '1+'
1816
        ),
1817
        'SUMSQ' => array(
1818
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1819
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMSQ',
1820
            'argumentCount' => '1+'
1821
        ),
1822
        'SUMX2MY2' => array(
1823
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1824
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMX2MY2',
1825
            'argumentCount' => '2'
1826
        ),
1827
        'SUMX2PY2' => array(
1828
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1829
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMX2PY2',
1830
            'argumentCount' => '2'
1831
        ),
1832
        'SUMXMY2' => array(
1833
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1834
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::SUMXMY2',
1835
            'argumentCount' => '2'
1836
        ),
1837
        'SYD' => array(
1838
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1839
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::SYD',
1840
            'argumentCount' => '4'
1841
        ),
1842
        'T' => array(
1843
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1844
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::RETURNSTRING',
1845
            'argumentCount' => '1'
1846
        ),
1847
        'TAN' => array(
1848
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1849
            'functionCall' => 'tan',
1850
            'argumentCount' => '1'
1851
        ),
1852
        'TANH' => array(
1853
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1854
            'functionCall' => 'tanh',
1855
            'argumentCount' => '1'
1856
        ),
1857
        'TBILLEQ' => array(
1858
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1859
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::TBILLEQ',
1860
            'argumentCount' => '3'
1861
        ),
1862
        'TBILLPRICE' => array(
1863
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1864
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::TBILLPRICE',
1865
            'argumentCount' => '3'
1866
        ),
1867
        'TBILLYIELD' => array(
1868
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1869
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::TBILLYIELD',
1870
            'argumentCount' => '3'
1871
        ),
1872
        'TDIST' => array(
1873
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1874
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::TDIST',
1875
            'argumentCount' => '3'
1876
        ),
1877
        'TEXT' => array(
1878
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1879
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::TEXTFORMAT',
1880
            'argumentCount' => '2'
1881
        ),
1882
        'TIME' => array(
1883
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1884
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::TIME',
1885
            'argumentCount' => '3'
1886
        ),
1887
        'TIMEVALUE' => array(
1888
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1889
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::TIMEVALUE',
1890
            'argumentCount' => '1'
1891
        ),
1892
        'TINV' => array(
1893
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1894
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::TINV',
1895
            'argumentCount' => '2'
1896
        ),
1897
        'TODAY' => array(
1898
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1899
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DATENOW',
1900
            'argumentCount' => '0'
1901
        ),
1902
        'TRANSPOSE' => array(
1903
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1904
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::TRANSPOSE',
1905
            'argumentCount' => '1'
1906
        ),
1907
        'TREND' => array(
1908
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1909
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::TREND',
1910
            'argumentCount' => '1-4'
1911
        ),
1912
        'TRIM' => array(
1913
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1914
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::TRIMSPACES',
1915
            'argumentCount' => '1'
1916
        ),
1917
        'TRIMMEAN' => array(
1918
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1919
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::TRIMMEAN',
1920
            'argumentCount' => '2'
1921
        ),
1922
        'TRUE' => array(
1923
            'category' => Calculation\Categories::CATEGORY_LOGICAL,
1924
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Logical::TRUE',
1925
            'argumentCount' => '0'
1926
        ),
1927
        'TRUNC' => array(
1928
            'category' => Calculation\Categories::CATEGORY_MATH_AND_TRIG,
1929
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\MathTrig::TRUNC',
1930
            'argumentCount' => '1,2'
1931
        ),
1932
        'TTEST' => array(
1933
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1934
            'functionCall' => 'Calculation\Categories::DUMMY',
1935
            'argumentCount' => '4'
1936
        ),
1937
        'TYPE' => array(
1938
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1939
            'functionCall' => 'Calculation\Categories::TYPE',
1940
            'argumentCount' => '1'
1941
        ),
1942
        'UPPER' => array(
1943
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1944
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::UPPERCASE',
1945
            'argumentCount' => '1'
1946
        ),
1947
        'USDOLLAR' => array(
1948
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1949
            'functionCall' => 'Calculation\Categories::DUMMY',
1950
            'argumentCount' => '2'
1951
        ),
1952
        'VALUE' => array(
1953
            'category' => Calculation\Categories::CATEGORY_TEXT_AND_DATA,
1954
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\TextData::VALUE',
1955
            'argumentCount' => '1'
1956
        ),
1957
        'VAR' => array(
1958
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1959
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::VARFunc',
1960
            'argumentCount' => '1+'
1961
        ),
1962
        'VARA' => array(
1963
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1964
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::VARA',
1965
            'argumentCount' => '1+'
1966
        ),
1967
        'VARP' => array(
1968
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1969
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::VARP',
1970
            'argumentCount' => '1+'
1971
        ),
1972
        'VARPA' => array(
1973
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
1974
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::VARPA',
1975
            'argumentCount' => '1+'
1976
        ),
1977
        'VDB' => array(
1978
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
1979
            'functionCall' => 'Calculation\Categories::DUMMY',
1980
            'argumentCount' => '5-7'
1981
        ),
1982
        'VERSION' => array(
1983
            'category' => Calculation\Categories::CATEGORY_INFORMATION,
1984
            'functionCall' => 'Calculation\Categories::VERSION',
1985
            'argumentCount' => '0'
1986
        ),
1987
        'VLOOKUP' => array(
1988
            'category' => Calculation\Categories::CATEGORY_LOOKUP_AND_REFERENCE,
1989
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\LookupRef::VLOOKUP',
1990
            'argumentCount' => '3,4'
1991
        ),
1992
        'WEEKDAY' => array(
1993
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1994
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::DAYOFWEEK',
1995
            'argumentCount' => '1,2'
1996
        ),
1997
        'WEEKNUM' => array(
1998
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
1999
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::WEEKOFYEAR',
2000
            'argumentCount' => '1,2'
2001
        ),
2002
        'WEIBULL' => array(
2003
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
2004
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::WEIBULL',
2005
            'argumentCount' => '4'
2006
        ),
2007
        'WORKDAY' => array(
2008
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
2009
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::WORKDAY',
2010
            'argumentCount' => '2+'
2011
        ),
2012
        'XIRR' => array(
2013
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
2014
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::XIRR',
2015
            'argumentCount' => '2,3'
2016
        ),
2017
        'XNPV' => array(
2018
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
2019
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::XNPV',
2020
            'argumentCount' => '3'
2021
        ),
2022
        'YEAR' => array(
2023
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
2024
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::YEAR',
2025
            'argumentCount' => '1'
2026
        ),
2027
        'YEARFRAC' => array(
2028
            'category' => Calculation\Categories::CATEGORY_DATE_AND_TIME,
2029
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\DateTime::YEARFRAC',
2030
            'argumentCount' => '2,3'
2031
        ),
2032
        'YIELD' => array(
2033
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
2034
            'functionCall' => 'Calculation\Categories::DUMMY',
2035
            'argumentCount' => '6,7'
2036
        ),
2037
        'YIELDDISC' => array(
2038
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
2039
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::YIELDDISC',
2040
            'argumentCount' => '4,5'
2041
        ),
2042
        'YIELDMAT' => array(
2043
            'category' => Calculation\Categories::CATEGORY_FINANCIAL,
2044
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Financial::YIELDMAT',
2045
            'argumentCount' => '5,6'
2046
        ),
2047
        'ZTEST' => array(
2048
            'category' => Calculation\Categories::CATEGORY_STATISTICAL,
2049
            'functionCall' => '\\PhpSpreadsheet\\Calculation\\Statistical::ZTEST',
2050
            'argumentCount' => '2-3'
2051
        )
2052
    );
2053
2054
    //    Internal functions used for special control purposes
2055
    private static $controlFunctions = array(
2056
        'MKMATRIX' => array(
2057
            'argumentCount' => '*',
2058
            'functionCall' => 'self::mkMatrix'
2059
        )
2060
    );
2061
2062
2063
    public function __construct(Spreadsheet $spreadsheet = null)
2064
    {
2065
        $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...
2066
2067
        $this->spreadsheet = $spreadsheet;
0 ignored issues
show
Documentation Bug introduced by
It seems like $spreadsheet can also be of type object<PhpSpreadsheet\Spreadsheet>. However, the property $spreadsheet is declared as type object<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...
2068
        $this->cyclicReferenceStack = new CalcEngine\CyclicReferenceStack();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \PhpSpreadsheet\Calc...\CyclicReferenceStack() of type object<PhpSpreadsheet\Ca...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...
2069
        $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...
2070
    }
2071
2072
2073
    private static function loadLocales()
2074
    {
2075
        $localeFileDirectory = PHPSPREADSHEET_ROOT.'PhpSpreadsheet/locale/';
2076
        foreach (glob($localeFileDirectory.'/*', GLOB_ONLYDIR) as $filename) {
2077
            $filename = substr($filename, strlen($localeFileDirectory)+1);
2078
            if ($filename != 'en') {
2079
                self::$validLocaleLanguages[] = $filename;
2080
            }
2081
        }
2082
    }
2083
2084
    /**
2085
     * Get an instance of this class
2086
     *
2087
     * @access    public
2088
     * @param   Spreadsheet $spreadsheet  Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
2089
     *                                    or NULL to create a standalone claculation engine
2090
     * @return Calculation
2091
     */
2092
    public static function getInstance(Spreadsheet $spreadsheet = null)
2093
    {
2094
        if ($spreadsheet !== null) {
2095
            $instance = $spreadsheet->getCalculationEngine();
2096
            if (isset($instance)) {
2097
                return $instance;
2098
            }
2099
        }
2100
2101
        if (!isset(self::$instance) || (self::$instance === null)) {
2102
            self::$instance = new \PhpSpreadsheet\Calculation();
2103
        }
2104
2105
        return self::$instance;
2106
    }
2107
2108
    /**
2109
     * Unset an instance of this class
2110
     *
2111
     * @access    public
2112
     * @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...
2113
     */
2114
    public function __destruct()
2115
    {
2116
        $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...
2117
    }
2118
2119
    /**
2120
     * Flush the calculation cache for any existing instance of this class
2121
     *        but only if a \PhpSpreadsheet\Calculation instance exists
2122
     *
2123
     * @access    public
2124
     * @return null
2125
     */
2126
    public function flushInstance()
2127
    {
2128
        $this->clearCalculationCache();
2129
    }
2130
2131
2132
    /**
2133
     * Get the debuglog for this claculation engine instance
2134
     *
2135
     * @access    public
2136
     * @return CalcEngine\Logger
2137
     */
2138
    public function getDebugLog()
2139
    {
2140
        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...
2141
    }
2142
2143
    /**
2144
     * __clone implementation. Cloning should not be allowed in a Singleton!
2145
     *
2146
     * @access    public
2147
     * @throws    Calculation\Exception
2148
     */
2149
    final public function __clone()
2150
    {
2151
        throw new Calculation\Exception('Cloning the calculation engine is not allowed!');
2152
    }
2153
2154
2155
    /**
2156
     * Return the locale-specific translation of TRUE
2157
     *
2158
     * @access    public
2159
     * @return     string        locale-specific translation of TRUE
2160
     */
2161
    public static function getTRUE()
2162
    {
2163
        return self::$localeBoolean['TRUE'];
2164
    }
2165
2166
    /**
2167
     * Return the locale-specific translation of FALSE
2168
     *
2169
     * @access    public
2170
     * @return     string        locale-specific translation of FALSE
2171
     */
2172
    public static function getFALSE()
2173
    {
2174
        return self::$localeBoolean['FALSE'];
2175
    }
2176
2177
    /**
2178
     * Set the Array Return Type (Array or Value of first element in the array)
2179
     *
2180
     * @access    public
2181
     * @param     string    $returnType            Array return type
2182
     * @return     boolean                    Success or failure
2183
     */
2184
    public static function setArrayReturnType($returnType)
2185
    {
2186
        if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
2187
            ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
2188
            ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
2189
            self::$returnArrayAsType = $returnType;
2190
            return true;
2191
        }
2192
        return false;
2193
    }
2194
2195
2196
    /**
2197
     * Return the Array Return Type (Array or Value of first element in the array)
2198
     *
2199
     * @access    public
2200
     * @return     string        $returnType            Array return type
2201
     */
2202
    public static function getArrayReturnType()
2203
    {
2204
        return self::$returnArrayAsType;
2205
    }
2206
2207
2208
    /**
2209
     * Is calculation caching enabled?
2210
     *
2211
     * @access    public
2212
     * @return boolean
2213
     */
2214
    public function getCalculationCacheEnabled()
2215
    {
2216
        return $this->calculationCacheEnabled;
2217
    }
2218
2219
    /**
2220
     * Enable/disable calculation cache
2221
     *
2222
     * @access    public
2223
     * @param boolean $pValue
2224
     */
2225
    public function setCalculationCacheEnabled($pValue = true)
2226
    {
2227
        $this->calculationCacheEnabled = $pValue;
2228
        $this->clearCalculationCache();
2229
    }
2230
2231
2232
    /**
2233
     * Enable calculation cache
2234
     */
2235
    public function enableCalculationCache()
2236
    {
2237
        $this->setCalculationCacheEnabled(true);
2238
    }
2239
2240
2241
    /**
2242
     * Disable calculation cache
2243
     */
2244
    public function disableCalculationCache()
2245
    {
2246
        $this->setCalculationCacheEnabled(false);
2247
    }
2248
2249
2250
    /**
2251
     * Clear calculation cache
2252
     */
2253
    public function clearCalculationCache()
2254
    {
2255
        $this->calculationCache = array();
2256
    }
2257
2258
    /**
2259
     * Clear calculation cache for a specified worksheet
2260
     *
2261
     * @param string $worksheetName
2262
     */
2263
    public function clearCalculationCacheForWorksheet($worksheetName)
2264
    {
2265
        if (isset($this->calculationCache[$worksheetName])) {
2266
            unset($this->calculationCache[$worksheetName]);
2267
        }
2268
    }
2269
2270
    /**
2271
     * Rename calculation cache for a specified worksheet
2272
     *
2273
     * @param string $fromWorksheetName
2274
     * @param string $toWorksheetName
2275
     */
2276
    public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName)
2277
    {
2278
        if (isset($this->calculationCache[$fromWorksheetName])) {
2279
            $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
2280
            unset($this->calculationCache[$fromWorksheetName]);
2281
        }
2282
    }
2283
2284
2285
    /**
2286
     * Get the currently defined locale code
2287
     *
2288
     * @return string
2289
     */
2290
    public function getLocale()
2291
    {
2292
        return self::$localeLanguage;
2293
    }
2294
2295
2296
    /**
2297
     * Set the locale code
2298
     *
2299
     * @param string $locale  The locale to use for formula translation
2300
     * @return boolean
2301
     */
2302
    public function setLocale($locale = 'en_us')
2303
    {
2304
        //    Identify our locale and language
2305
        $language = $locale = strtolower($locale);
2306
        if (strpos($locale, '_') !== false) {
2307
            list($language) = explode('_', $locale);
2308
        }
2309
2310
        if (count(self::$validLocaleLanguages) == 1) {
2311
            self::loadLocales();
2312
        }
2313
        //    Test whether we have any language data for this language (any locale)
2314
        if (in_array($language, self::$validLocaleLanguages)) {
2315
            //    initialise language/locale settings
2316
            self::$localeFunctions = array();
2317
            self::$localeArgumentSeparator = ',';
2318
            self::$localeBoolean = array('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...
2319
            //    Default is English, if user isn't requesting english, then read the necessary data from the locale files
2320
            if ($locale != 'en_us') {
2321
                //    Search for a file with a list of function names for locale
2322
                $functionNamesFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'functions';
2323
                if (!file_exists($functionNamesFile)) {
2324
                    //    If there isn't a locale specific function file, look for a language specific function file
2325
                    $functionNamesFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions';
2326
                    if (!file_exists($functionNamesFile)) {
2327
                        return false;
2328
                    }
2329
                }
2330
                //    Retrieve the list of locale or language specific function names
2331
                $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2332
                foreach ($localeFunctions as $localeFunction) {
2333
                    list($localeFunction) = explode('##', $localeFunction);    //    Strip out comments
2334
                    if (strpos($localeFunction, '=') !== false) {
2335
                        list($fName, $lfName) = explode('=', $localeFunction);
2336
                        $fName = trim($fName);
2337
                        $lfName = trim($lfName);
2338
                        if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
2339
                            self::$localeFunctions[$fName] = $lfName;
2340
                        }
2341
                    }
2342
                }
2343
                //    Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
2344
                if (isset(self::$localeFunctions['TRUE'])) {
2345
                    self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE'];
2346
                }
2347
                if (isset(self::$localeFunctions['FALSE'])) {
2348
                    self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
2349
                }
2350
2351
                $configFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'config';
2352
                if (!file_exists($configFile)) {
2353
                    $configFile = PHPSPREADSHEET_ROOT . 'PhpSpreadsheet'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'config';
2354
                }
2355
                if (file_exists($configFile)) {
2356
                    $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2357
                    foreach ($localeSettings as $localeSetting) {
2358
                        list($localeSetting) = explode('##', $localeSetting);    //    Strip out comments
2359
                        if (strpos($localeSetting, '=') !== false) {
2360
                            list($settingName, $settingValue) = explode('=', $localeSetting);
2361
                            $settingName = strtoupper(trim($settingName));
2362
                            switch ($settingName) {
2363
                                case 'ARGUMENTSEPARATOR':
2364
                                    self::$localeArgumentSeparator = trim($settingValue);
2365
                                    break;
2366
                            }
2367
                        }
2368
                    }
2369
                }
2370
            }
2371
2372
            self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
2373
            self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
2374
            self::$localeLanguage = $locale;
2375
            return true;
2376
        }
2377
        return false;
2378
    }
2379
2380
2381
2382
    public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
2383
    {
2384
        $strlen = mb_strlen($formula);
2385
        for ($i = 0; $i < $strlen; ++$i) {
2386
            $chr = mb_substr($formula, $i, 1);
2387
            switch ($chr) {
2388
                case '{':
2389
                    $inBraces = true;
2390
                    break;
2391
                case '}':
2392
                    $inBraces = false;
2393
                    break;
2394
                case $fromSeparator:
2395
                    if (!$inBraces) {
2396
                        $formula = mb_substr($formula, 0, $i).$toSeparator.mb_substr($formula, $i+1);
2397
                    }
2398
            }
2399
        }
2400
        return $formula;
2401
    }
2402
2403
    private static function translateFormula($from, $to, $formula, $fromSeparator, $toSeparator)
2404
    {
2405
        //    Convert any Excel function names to the required language
2406
        if (self::$localeLanguage !== 'en_us') {
2407
            $inBraces = false;
2408
            //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
2409
            if (strpos($formula, '"') !== false) {
2410
                //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
2411
                //        the formula
2412
                $temp = explode('"', $formula);
2413
                $i = false;
2414
                foreach ($temp as &$value) {
2415
                    //    Only count/replace in alternating array entries
2416 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...
2417
                        $value = preg_replace($from, $to, $value);
2418
                        $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces);
2419
                    }
2420
                }
2421
                unset($value);
2422
                //    Then rebuild the formula string
2423
                $formula = implode('"', $temp);
2424 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...
2425
                //    If there's no quoted strings, then we do a simple count/replace
2426
                $formula = preg_replace($from, $to, $formula);
2427
                $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inBraces);
2428
            }
2429
        }
2430
2431
        return $formula;
2432
    }
2433
2434
    private static $functionReplaceFromExcel = null;
2435
    private static $functionReplaceToLocale  = null;
2436
2437 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...
2438
    {
2439
        if (self::$functionReplaceFromExcel === null) {
2440
            self::$functionReplaceFromExcel = array();
2441
            foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2442
                self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui';
2443
            }
2444
            foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2445
                self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
2446
            }
2447
        }
2448
2449
        if (self::$functionReplaceToLocale === null) {
2450
            self::$functionReplaceToLocale = array();
2451
            foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2452
                self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2';
2453
            }
2454
            foreach (array_values(self::$localeBoolean) as $localeBoolean) {
2455
                self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2';
2456
            }
2457
        }
2458
2459
        return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
2460
    }
2461
2462
2463
    private static $functionReplaceFromLocale = null;
2464
    private static $functionReplaceToExcel    = null;
2465
2466 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...
2467
    {
2468
        if (self::$functionReplaceFromLocale === null) {
2469
            self::$functionReplaceFromLocale = array();
2470
            foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2471
                self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui';
2472
            }
2473
            foreach (array_values(self::$localeBoolean) as $excelBoolean) {
2474
                self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
2475
            }
2476
        }
2477
2478
        if (self::$functionReplaceToExcel === null) {
2479
            self::$functionReplaceToExcel = array();
2480
            foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2481
                self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2';
2482
            }
2483
            foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2484
                self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2';
2485
            }
2486
        }
2487
2488
        return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
2489
    }
2490
2491
2492
    public static function localeFunc($function)
2493
    {
2494
        if (self::$localeLanguage !== 'en_us') {
2495
            $functionName = trim($function, '(');
2496
            if (isset(self::$localeFunctions[$functionName])) {
2497
                $brace = ($functionName != $function);
2498
                $function = self::$localeFunctions[$functionName];
2499
                if ($brace) {
2500
                    $function .= '(';
2501
                }
2502
            }
2503
        }
2504
        return $function;
2505
    }
2506
2507
2508
2509
2510
    /**
2511
     * Wrap string values in quotes
2512
     *
2513
     * @param mixed $value
2514
     * @return mixed
2515
     */
2516
    public static function wrapResult($value)
2517
    {
2518
        if (is_string($value)) {
2519
            //    Error values cannot be "wrapped"
2520
            if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) {
2521
                //    Return Excel errors "as is"
2522
                return $value;
2523
            }
2524
            //    Return strings wrapped in quotes
2525
            return '"'.$value.'"';
2526
        //    Convert numeric errors to NaN error
2527
        } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2528
            return Calculation\Functions::NAN();
2529
        }
2530
2531
        return $value;
2532
    }
2533
2534
2535
    /**
2536
     * Remove quotes used as a wrapper to identify string values
2537
     *
2538
     * @param mixed $value
2539
     * @return mixed
2540
     */
2541
    public static function unwrapResult($value)
2542
    {
2543
        if (is_string($value)) {
2544
            if ((isset($value{0})) && ($value{0} == '"') && (substr($value, -1) == '"')) {
2545
                return substr($value, 1, -1);
2546
            }
2547
        //    Convert numeric errors to NAN error
2548
        } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2549
            return Calculation\Functions::NAN();
2550
        }
2551
        return $value;
2552
    }
2553
2554
2555
2556
2557
    /**
2558
     * Calculate cell value (using formula from a cell ID)
2559
     * Retained for backward compatibility
2560
     *
2561
     * @access    public
2562
     * @param    Cell    $pCell    Cell to calculate
2563
     * @return    mixed
2564
     * @throws    Calculation\Exception
2565
     */
2566
    public function calculate(Cell $pCell = null)
2567
    {
2568
        try {
2569
            return $this->calculateCellValue($pCell);
2570
        } catch (Exception $e) {
2571
            throw new Calculation\Exception($e->getMessage());
2572
        }
2573
    }
2574
2575
2576
    /**
2577
     * Calculate the value of a cell formula
2578
     *
2579
     * @access    public
2580
     * @param    Cell    $pCell        Cell to calculate
2581
     * @param    Boolean            $resetLog    Flag indicating whether the debug log should be reset or not
2582
     * @return    mixed
2583
     * @throws    Calculation\Exception
2584
     */
2585
    public function calculateCellValue(Cell $pCell = null, $resetLog = true)
2586
    {
2587
        if ($pCell === null) {
2588
            return null;
2589
        }
2590
2591
        $returnArrayAsType = self::$returnArrayAsType;
2592
        if ($resetLog) {
2593
            //    Initialise the logging settings if requested
2594
            $this->formulaError = null;
2595
            $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...
2596
            $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...
2597
            $this->cyclicFormulaCounter = 1;
2598
2599
            self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
2600
        }
2601
2602
        //    Execute the calculation for the cell formula
2603
        $this->cellStack[] = array(
2604
            'sheet' => $pCell->getWorksheet()->getTitle(),
2605
            'cell' => $pCell->getCoordinate(),
2606
        );
2607
        try {
2608
            $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
2609
            $cellAddress = array_pop($this->cellStack);
2610
            $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2611
        } catch (Exception $e) {
2612
            $cellAddress = array_pop($this->cellStack);
2613
            $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2614
            throw new Calculation\Exception($e->getMessage());
2615
        }
2616
2617
        if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
2618
            self::$returnArrayAsType = $returnArrayAsType;
2619
            $testResult = Calculation\Functions::flattenArray($result);
2620
            if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
2621
                return Calculation\Functions::VALUE();
2622
            }
2623
            //    If there's only a single cell in the array, then we allow it
2624
            if (count($testResult) != 1) {
2625
                //    If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
2626
                $r = array_keys($result);
2627
                $r = array_shift($r);
2628
                if (!is_numeric($r)) {
2629
                    return Calculation\Functions::VALUE();
2630
                }
2631
                if (is_array($result[$r])) {
2632
                    $c = array_keys($result[$r]);
2633
                    $c = array_shift($c);
2634
                    if (!is_numeric($c)) {
2635
                        return Calculation\Functions::VALUE();
2636
                    }
2637
                }
2638
            }
2639
            $result = array_shift($testResult);
2640
        }
2641
        self::$returnArrayAsType = $returnArrayAsType;
2642
2643
2644
        if ($result === null) {
2645
            return 0;
2646
        } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
2647
            return Calculation\Functions::NAN();
2648
        }
2649
        return $result;
2650
    }
2651
2652
2653
    /**
2654
     * Validate and parse a formula string
2655
     *
2656
     * @param    string        $formula        Formula to parse
2657
     * @return    array
2658
     * @throws    Calculation\Exception
2659
     */
2660
    public function parseFormula($formula)
2661
    {
2662
        //    Basic validation that this is indeed a formula
2663
        //    We return an empty array if not
2664
        $formula = trim($formula);
2665
        if ((!isset($formula{0})) || ($formula{0} != '=')) {
2666
            return array();
2667
        }
2668
        $formula = ltrim(substr($formula, 1));
2669
        if (!isset($formula{0})) {
2670
            return array();
2671
        }
2672
2673
        //    Parse the formula and return the token stack
2674
        return $this->_parseFormula($formula);
2675
    }
2676
2677
2678
    /**
2679
     * Calculate the value of a formula
2680
     *
2681
     * @param    string            $formula    Formula to parse
2682
     * @param    string            $cellID        Address of the cell to calculate
2683
     * @param    Cell    $pCell        Cell to calculate
2684
     * @return    mixed
2685
     * @throws    Calculation\Exception
2686
     */
2687
    public function calculateFormula($formula, $cellID = null, Cell $pCell = null)
2688
    {
2689
        //    Initialise the logging settings
2690
        $this->formulaError = null;
2691
        $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...
2692
        $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...
2693
2694
        if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
2695
            $cellID = 'A1';
2696
            $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
2697
        } else {
2698
            //    Disable calculation cacheing because it only applies to cell calculations, not straight formulae
2699
            //    But don't actually flush any cache
2700
            $resetCache = $this->getCalculationCacheEnabled();
2701
            $this->calculationCacheEnabled = false;
2702
        }
2703
2704
        //    Execute the calculation
2705
        try {
2706
            $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
2707
        } catch (Exception $e) {
2708
            throw new Calculation\Exception($e->getMessage());
2709
        }
2710
2711
        if ($this->spreadsheet === null) {
2712
            //    Reset calculation cacheing to its previous state
2713
            $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...
2714
        }
2715
2716
        return $result;
2717
    }
2718
2719
2720
    public function getValueFromCache($cellReference, &$cellValue)
2721
    {
2722
        // Is calculation cacheing enabled?
2723
        // Is the value present in calculation cache?
2724
        $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...
2725
        if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
2726
            $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...
2727
            // Return the cached result
2728
            $cellValue = $this->calculationCache[$cellReference];
2729
            return true;
2730
        }
2731
        return false;
2732
    }
2733
2734
    public function saveValueToCache($cellReference, $cellValue)
2735
    {
2736
        if ($this->calculationCacheEnabled) {
2737
            $this->calculationCache[$cellReference] = $cellValue;
2738
        }
2739
    }
2740
2741
    /**
2742
     * Parse a cell formula and calculate its value
2743
     *
2744
     * @param    string            $formula    The formula to parse and calculate
2745
     * @param    string            $cellID        The ID (e.g. A3) of the cell that we are calculating
2746
     * @param    Cell    $pCell        Cell to calculate
2747
     * @return   mixed
2748
     * @throws   Calculation\Exception
2749
     */
2750
    public function _calculateFormulaValue($formula, $cellID = null, Cell $pCell = null)
2751
    {
2752
        $cellValue = null;
2753
2754
        //    Basic validation that this is indeed a formula
2755
        //    We simply return the cell value if not
2756
        $formula = trim($formula);
2757
        if ($formula{0} != '=') {
2758
            return self::wrapResult($formula);
2759
        }
2760
        $formula = ltrim(substr($formula, 1));
2761
        if (!isset($formula{0})) {
2762
            return self::wrapResult($formula);
2763
        }
2764
2765
        $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
2766
        $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
2767
        $wsCellReference = $wsTitle . '!' . $cellID;
2768
2769
        if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
2770
            return $cellValue;
2771
        }
2772
2773
        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...
2774
            if ($this->cyclicFormulaCount <= 0) {
2775
                $this->cyclicFormulaCell = '';
2776
                return $this->raiseFormulaError('Cyclic Reference in Formula');
2777
            } elseif ($this->cyclicFormulaCell === $wsCellReference) {
2778
                ++$this->cyclicFormulaCounter;
2779
                if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2780
                    $this->cyclicFormulaCell = '';
2781
                    return $cellValue;
2782
                }
2783
            } elseif ($this->cyclicFormulaCell == '') {
2784
                if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2785
                    return $cellValue;
2786
                }
2787
                $this->cyclicFormulaCell = $wsCellReference;
2788
            }
2789
        }
2790
2791
        //    Parse the formula onto the token stack and calculate the value
2792
        $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...
2793
        $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
2794
        $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...
2795
2796
        // Save to calculation cache
2797
        if ($cellID !== null) {
2798
            $this->saveValueToCache($wsCellReference, $cellValue);
2799
        }
2800
2801
        //    Return the calculated value
2802
        return $cellValue;
2803
    }
2804
2805
2806
    /**
2807
     * Ensure that paired matrix operands are both matrices and of the same size
2808
     *
2809
     * @param    mixed        &$operand1    First matrix operand
2810
     * @param    mixed        &$operand2    Second matrix operand
2811
     * @param    integer        $resize        Flag indicating whether the matrices should be resized to match
2812
     *                                        and (if so), whether the smaller dimension should grow or the
2813
     *                                        larger should shrink.
2814
     *                                            0 = no resize
2815
     *                                            1 = shrink to fit
2816
     *                                            2 = extend to fit
2817
     */
2818
    private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1)
2819
    {
2820
        //    Examine each of the two operands, and turn them into an array if they aren't one already
2821
        //    Note that this function should only be called if one or both of the operand is already an array
2822
        if (!is_array($operand1)) {
2823
            list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2);
2824
            $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
2825
            $resize = 0;
2826
        } elseif (!is_array($operand2)) {
2827
            list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1);
2828
            $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
2829
            $resize = 0;
2830
        }
2831
2832
        list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1);
2833
        list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2);
2834
        if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
2835
            $resize = 1;
2836
        }
2837
2838
        if ($resize == 2) {
2839
            //    Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
2840
            self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2841
        } elseif ($resize == 1) {
2842
            //    Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
2843
            self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2844
        }
2845
        return array( $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2846
    }
2847
2848
2849
    /**
2850
     * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
2851
     *
2852
     * @param    mixed        &$matrix        matrix operand
2853
     * @return    array        An array comprising the number of rows, and number of columns
2854
     */
2855
    private static function getMatrixDimensions(&$matrix)
2856
    {
2857
        $matrixRows = count($matrix);
2858
        $matrixColumns = 0;
2859
        foreach ($matrix as $rowKey => $rowValue) {
2860
            $matrixColumns = max(count($rowValue), $matrixColumns);
2861
            if (!is_array($rowValue)) {
2862
                $matrix[$rowKey] = array($rowValue);
2863
            } else {
2864
                $matrix[$rowKey] = array_values($rowValue);
2865
            }
2866
        }
2867
        $matrix = array_values($matrix);
2868
        return array($matrixRows, $matrixColumns);
2869
    }
2870
2871
2872
    /**
2873
     * Ensure that paired matrix operands are both matrices of the same size
2874
     *
2875
     * @param    mixed        &$matrix1        First matrix operand
2876
     * @param    mixed        &$matrix2        Second matrix operand
2877
     * @param    integer        $matrix1Rows    Row size of first matrix operand
2878
     * @param    integer        $matrix1Columns    Column size of first matrix operand
2879
     * @param    integer        $matrix2Rows    Row size of second matrix operand
2880
     * @param    integer        $matrix2Columns    Column size of second matrix operand
2881
     */
2882
    private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2883
    {
2884 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...
2885
            if ($matrix2Rows < $matrix1Rows) {
2886
                for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
2887
                    unset($matrix1[$i]);
2888
                }
2889
            }
2890
            if ($matrix2Columns < $matrix1Columns) {
2891
                for ($i = 0; $i < $matrix1Rows; ++$i) {
2892
                    for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2893
                        unset($matrix1[$i][$j]);
2894
                    }
2895
                }
2896
            }
2897
        }
2898
2899 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...
2900
            if ($matrix1Rows < $matrix2Rows) {
2901
                for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
2902
                    unset($matrix2[$i]);
2903
                }
2904
            }
2905
            if ($matrix1Columns < $matrix2Columns) {
2906
                for ($i = 0; $i < $matrix2Rows; ++$i) {
2907
                    for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2908
                        unset($matrix2[$i][$j]);
2909
                    }
2910
                }
2911
            }
2912
        }
2913
    }
2914
2915
2916
    /**
2917
     * Ensure that paired matrix operands are both matrices of the same size
2918
     *
2919
     * @param    mixed        &$matrix1    First matrix operand
2920
     * @param    mixed        &$matrix2    Second matrix operand
2921
     * @param    integer        $matrix1Rows    Row size of first matrix operand
2922
     * @param    integer        $matrix1Columns    Column size of first matrix operand
2923
     * @param    integer        $matrix2Rows    Row size of second matrix operand
2924
     * @param    integer        $matrix2Columns    Column size of second matrix operand
2925
     */
2926
    private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2927
    {
2928 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...
2929
            if ($matrix2Columns < $matrix1Columns) {
2930
                for ($i = 0; $i < $matrix2Rows; ++$i) {
2931
                    $x = $matrix2[$i][$matrix2Columns-1];
2932
                    for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2933
                        $matrix2[$i][$j] = $x;
2934
                    }
2935
                }
2936
            }
2937
            if ($matrix2Rows < $matrix1Rows) {
2938
                $x = $matrix2[$matrix2Rows-1];
2939
                for ($i = 0; $i < $matrix1Rows; ++$i) {
2940
                    $matrix2[$i] = $x;
2941
                }
2942
            }
2943
        }
2944
2945 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...
2946
            if ($matrix1Columns < $matrix2Columns) {
2947
                for ($i = 0; $i < $matrix1Rows; ++$i) {
2948
                    $x = $matrix1[$i][$matrix1Columns-1];
2949
                    for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2950
                        $matrix1[$i][$j] = $x;
2951
                    }
2952
                }
2953
            }
2954
            if ($matrix1Rows < $matrix2Rows) {
2955
                $x = $matrix1[$matrix1Rows-1];
2956
                for ($i = 0; $i < $matrix2Rows; ++$i) {
2957
                    $matrix1[$i] = $x;
2958
                }
2959
            }
2960
        }
2961
    }
2962
2963
2964
    /**
2965
     * Format details of an operand for display in the log (based on operand type)
2966
     *
2967
     * @param    mixed        $value    First matrix operand
2968
     * @return    mixed
2969
     */
2970
    private function showValue($value)
2971
    {
2972
        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...
2973
            $testArray = Calculation\Functions::flattenArray($value);
2974
            if (count($testArray) == 1) {
2975
                $value = array_pop($testArray);
2976
            }
2977
2978
            if (is_array($value)) {
2979
                $returnMatrix = array();
2980
                $pad = $rpad = ', ';
2981
                foreach ($value as $row) {
2982
                    if (is_array($row)) {
2983
                        $returnMatrix[] = implode($pad, array_map(array($this, 'showValue'), $row));
2984
                        $rpad = '; ';
2985
                    } else {
2986
                        $returnMatrix[] = $this->showValue($row);
2987
                    }
2988
                }
2989
                return '{ '.implode($rpad, $returnMatrix).' }';
2990
            } elseif (is_string($value) && (trim($value, '"') == $value)) {
2991
                return '"'.$value.'"';
2992
            } elseif (is_bool($value)) {
2993
                return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
2994
            }
2995
        }
2996
        return Calculation\Functions::flattenSingleValue($value);
2997
    }
2998
2999
3000
    /**
3001
     * Format type and details of an operand for display in the log (based on operand type)
3002
     *
3003
     * @param    mixed        $value    First matrix operand
3004
     * @return    mixed
3005
     */
3006
    private function showTypeDetails($value)
3007
    {
3008
        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...
3009
            $testArray = Calculation\Functions::flattenArray($value);
3010
            if (count($testArray) == 1) {
3011
                $value = array_pop($testArray);
3012
            }
3013
3014
            if ($value === null) {
3015
                return 'a NULL value';
3016
            } elseif (is_float($value)) {
3017
                $typeString = 'a floating point number';
3018
            } elseif (is_int($value)) {
3019
                $typeString = 'an integer number';
3020
            } elseif (is_bool($value)) {
3021
                $typeString = 'a boolean';
3022
            } elseif (is_array($value)) {
3023
                $typeString = 'a matrix';
3024
            } else {
3025
                if ($value == '') {
3026
                    return 'an empty string';
3027
                } elseif ($value{0} == '#') {
3028
                    return 'a '.$value.' error';
3029
                } else {
3030
                    $typeString = 'a string';
3031
                }
3032
            }
3033
            return $typeString.' with a value of '.$this->showValue($value);
3034
        }
3035
    }
3036
3037
3038
    private function convertMatrixReferences($formula)
3039
    {
3040
        static $matrixReplaceFrom = array('{', ';', '}');
3041
        static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))');
3042
3043
        //    Convert any Excel matrix references to the MKMATRIX() function
3044
        if (strpos($formula, '{') !== false) {
3045
            //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
3046
            if (strpos($formula, '"') !== false) {
3047
                //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
3048
                //        the formula
3049
                $temp = explode('"', $formula);
3050
                //    Open and Closed counts used for trapping mismatched braces in the formula
3051
                $openCount = $closeCount = 0;
3052
                $i = false;
3053
                foreach ($temp as &$value) {
3054
                    //    Only count/replace in alternating array entries
3055
                    if ($i = !$i) {
3056
                        $openCount += substr_count($value, '{');
3057
                        $closeCount += substr_count($value, '}');
3058
                        $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
3059
                    }
3060
                }
3061
                unset($value);
3062
                //    Then rebuild the formula string
3063
                $formula = implode('"', $temp);
3064
            } else {
3065
                //    If there's no quoted strings, then we do a simple count/replace
3066
                $openCount = substr_count($formula, '{');
3067
                $closeCount = substr_count($formula, '}');
3068
                $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
3069
            }
3070
            //    Trap for mismatched braces and trigger an appropriate error
3071
            if ($openCount < $closeCount) {
3072
                if ($openCount > 0) {
3073
                    return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
3074
                } else {
3075
                    return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered");
3076
                }
3077
            } elseif ($openCount > $closeCount) {
3078
                if ($closeCount > 0) {
3079
                    return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
3080
                } else {
3081
                    return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered");
3082
                }
3083
            }
3084
        }
3085
3086
        return $formula;
3087
    }
3088
3089
3090
    private static function mkMatrix()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
3091
    {
3092
        return func_get_args();
3093
    }
3094
3095
3096
    //    Binary Operators
3097
    //    These operators always work on two values
3098
    //    Array key is the operator, the value indicates whether this is a left or right associative operator
3099
    private static $operatorAssociativity    = array(
3100
        '^' => 0,                                                            //    Exponentiation
3101
        '*' => 0, '/' => 0,                                                 //    Multiplication and Division
3102
        '+' => 0, '-' => 0,                                                    //    Addition and Subtraction
3103
        '&' => 0,                                                            //    Concatenation
3104
        '|' => 0, ':' => 0,                                                    //    Intersect and Range
3105
        '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0        //    Comparison
3106
    );
3107
3108
    //    Comparison (Boolean) Operators
3109
    //    These operators work on two values, but always return a boolean result
3110
    private static $comparisonOperators    = array('>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true);
3111
3112
    //    Operator Precedence
3113
    //    This list includes all valid operators, whether binary (including boolean) or unary (such as %)
3114
    //    Array key is the operator, the value is its precedence
3115
    private static $operatorPrecedence    = array(
3116
        ':' => 8,                                                                //    Range
3117
        '|' => 7,                                                                //    Intersect
3118
        '~' => 6,                                                                //    Negation
3119
        '%' => 5,                                                                //    Percentage
3120
        '^' => 4,                                                                //    Exponentiation
3121
        '*' => 3, '/' => 3,                                                     //    Multiplication and Division
3122
        '+' => 2, '-' => 2,                                                        //    Addition and Subtraction
3123
        '&' => 1,                                                                //    Concatenation
3124
        '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0            //    Comparison
3125
    );
3126
3127
    // Convert infix to postfix notation
3128
    private function _parseFormula($formula, Cell $pCell = null)
3129
    {
3130
        if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
3131
            return false;
3132
        }
3133
3134
        //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
3135
        //        so we store the parent worksheet so that we can re-attach it when necessary
3136
        $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
3137
3138
        $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
3139
                               '|'.self::CALCULATION_REGEXP_CELLREF.
3140
                               '|'.self::CALCULATION_REGEXP_NUMBER.
3141
                               '|'.self::CALCULATION_REGEXP_STRING.
3142
                               '|'.self::CALCULATION_REGEXP_OPENBRACE.
3143
                               '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
3144
                               '|'.self::CALCULATION_REGEXP_ERROR.
3145
                             ')/si';
3146
3147
        //    Start with initialisation
3148
        $index = 0;
3149
        $stack = new Calculation\Token\Stack;
3150
        $output = array();
3151
        $expectingOperator = false;                    //    We use this test in syntax-checking the expression to determine when a
3152
                                                    //        - is a negation or + is a positive operator rather than an operation
3153
        $expectingOperand = false;                    //    We use this test in syntax-checking the expression to determine whether an operand
3154
                                                    //        should be null in a function call
3155
        //    The guts of the lexical parser
3156
        //    Loop through the formula extracting each operator and operand in turn
3157
        while (true) {
3158
//echo 'Assessing Expression '.substr($formula, $index), PHP_EOL;
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...
3159
            $opCharacter = $formula{$index};    //    Get the first character of the value at the current index position
3160
//echo 'Initial character of expression block is '.$opCharacter, PHP_EOL;
3161
            if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula{$index+1}]))) {
3162
                $opCharacter .= $formula{++$index};
3163
//echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3164
            }
3165
3166
            //    Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
3167
            $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
3168
//echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% 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...
3169
//var_dump($match);
3170
3171
            if ($opCharacter == '-' && !$expectingOperator) {                //    Is it a negation instead of a minus?
3172
//echo 'Element is a Negation operator', PHP_EOL;
3173
                $stack->push('Unary Operator', '~');                            //    Put a negation on the stack
3174
                ++$index;                                                    //        and drop the negation symbol
3175
            } elseif ($opCharacter == '%' && $expectingOperator) {
3176
//echo 'Element is a Percentage operator', PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
3177
                $stack->push('Unary Operator', '%');                            //    Put a percentage on the stack
3178
                ++$index;
3179
            } elseif ($opCharacter == '+' && !$expectingOperator) {            //    Positive (unary plus rather than binary operator plus) can be discarded?
3180
//echo 'Element is a Positive number, not Plus operator', PHP_EOL;
3181
                ++$index;                                                    //    Drop the redundant plus symbol
3182
            } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) {    //    We have to explicitly deny a tilde or pipe, because they are legal
3183
                return $this->raiseFormulaError("Formula Error: Illegal character '~'");                //        on the stack but not in the input expression
3184
            } 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...
3185
//echo 'Element with value '.$opCharacter.' is an Operator', PHP_EOL;
3186 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...
3187
                    ($o2 = $stack->last()) &&
3188
                    isset(self::$operators[$o2['value']]) &&
3189
                    @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3190
                    $output[] = $stack->pop();                                //    Swap operands and higher precedence operators from the stack to the output
3191
                }
3192
                $stack->push('Binary Operator', $opCharacter);    //    Finally put our current operator onto the stack
3193
                ++$index;
3194
                $expectingOperator = false;
3195
            } elseif ($opCharacter == ')' && $expectingOperator) {            //    Are we expecting to close a parenthesis?
3196
//echo 'Element is a Closing bracket', PHP_EOL;
3197
                $expectingOperand = false;
3198 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...
3199
                    if ($o2 === null) {
3200
                        return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"');
3201
                    } else {
3202
                        $output[] = $o2;
3203
                    }
3204
                }
3205
                $d = $stack->last(2);
3206
                if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {    //    Did this parenthesis just close a function?
3207
                    $functionName = $matches[1];                                        //    Get the function name
3208
//echo 'Closed Function is '.$functionName, PHP_EOL;
3209
                    $d = $stack->pop();
3210
                    $argumentCount = $d['value'];        //    See how many arguments there were (argument count is the next value stored on the stack)
3211
//if ($argumentCount == 0) {
3212
//    echo 'With no arguments', PHP_EOL;
3213
//} elseif ($argumentCount == 1) {
3214
//    echo 'With 1 argument', PHP_EOL;
3215
//} else {
3216
//    echo 'With '.$argumentCount.' arguments', PHP_EOL;
3217
//}
3218
                    $output[] = $d;                        //    Dump the argument count on the output
3219
                    $output[] = $stack->pop();            //    Pop the function and push onto the output
3220
                    if (isset(self::$controlFunctions[$functionName])) {
3221
//echo 'Built-in function '.$functionName, PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3222
                        $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
3223
                        $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...
3224
                    } elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) {
3225
//echo 'PhpSpreadsheet function '.$functionName, PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3226
                        $expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount'];
3227
                        $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...
3228
                    } else {    // did we somehow push a non-function on the stack? this should never happen
3229
                        return $this->raiseFormulaError("Formula Error: Internal error, non-function on stack");
3230
                    }
3231
                    //    Check the argument count
3232
                    $argumentCountError = false;
3233
                    if (is_numeric($expectedArgumentCount)) {
3234
                        if ($expectedArgumentCount < 0) {
3235
//echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount), PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
3236
                            if ($argumentCount > abs($expectedArgumentCount)) {
3237
                                $argumentCountError = true;
3238
                                $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
3239
                            }
3240
                        } else {
3241
//echo '$expectedArgumentCount is numeric '.$expectedArgumentCount, PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3242
                            if ($argumentCount != $expectedArgumentCount) {
3243
                                $argumentCountError = true;
3244
                                $expectedArgumentCountString = $expectedArgumentCount;
3245
                            }
3246
                        }
3247
                    } elseif ($expectedArgumentCount != '*') {
3248
                        $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...
3249
//print_r($argMatch);
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...
3250
//echo PHP_EOL;
3251
                        switch ($argMatch[2]) {
3252
                            case '+':
3253
                                if ($argumentCount < $argMatch[1]) {
3254
                                    $argumentCountError = true;
3255
                                    $expectedArgumentCountString = $argMatch[1].' or more ';
3256
                                }
3257
                                break;
3258 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...
3259
                                if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
3260
                                    $argumentCountError = true;
3261
                                    $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3];
3262
                                }
3263
                                break;
3264 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...
3265
                                if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
3266
                                    $argumentCountError = true;
3267
                                    $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3];
3268
                                }
3269
                                break;
3270
                        }
3271
                    }
3272
                    if ($argumentCountError) {
3273
                        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...
3274
                    }
3275
                }
3276
                ++$index;
3277
            } elseif ($opCharacter == ',') {            //    Is this the separator for function arguments?
3278
//echo 'Element is a Function argument separator', PHP_EOL;
3279 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...
3280
                    if ($o2 === null) {
3281
                        return $this->raiseFormulaError("Formula Error: Unexpected ,");
3282
                    } else {
3283
                        $output[] = $o2;    // pop the argument expression stuff and push onto the output
3284
                    }
3285
                }
3286
                //    If we've a comma when we're expecting an operand, then what we actually have is a null operand;
3287
                //        so push a null onto the stack
3288
                if (($expectingOperand) || (!$expectingOperator)) {
3289
                    $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
3290
                }
3291
                // make sure there was a function
3292
                $d = $stack->last(2);
3293
                if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {
3294
                    return $this->raiseFormulaError("Formula Error: Unexpected ,");
3295
                }
3296
                $d = $stack->pop();
3297
                $stack->push($d['type'], ++$d['value'], $d['reference']);    // increment the argument count
3298
                $stack->push('Brace', '(');    // put the ( back on, we'll need to pop back to it again
3299
                $expectingOperator = false;
3300
                $expectingOperand = true;
3301
                ++$index;
3302
            } elseif ($opCharacter == '(' && !$expectingOperator) {
3303
//                echo 'Element is an Opening Bracket<br />';
3304
                $stack->push('Brace', '(');
3305
                ++$index;
3306
            } elseif ($isOperandOrFunction && !$expectingOperator) {    // do we now have a function/variable/number?
3307
                $expectingOperator = true;
3308
                $expectingOperand = false;
3309
                $val = $match[1];
3310
                $length = strlen($val);
3311
//                echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3312
3313
                if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) {
3314
                    $val = preg_replace('/\s/u', '', $val);
3315
//                    echo 'Element '.$val.' is a Function<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3316
                    if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) {    // it's a function
3317
                        $stack->push('Function', strtoupper($val));
3318
                        $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index+$length), $amatch);
3319
                        if ($ax) {
3320
                            $stack->push('Operand Count for Function '.strtoupper($val).')', 0);
3321
                            $expectingOperator = true;
3322
                        } else {
3323
                            $stack->push('Operand Count for Function '.strtoupper($val).')', 1);
3324
                            $expectingOperator = false;
3325
                        }
3326
                        $stack->push('Brace', '(');
3327
                    } else {    // it's a var w/ implicit multiplication
3328
                        $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => null);
3329
                    }
3330
                } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) {
3331
//                    echo 'Element '.$val.' is a Cell reference<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3332
                    //    Watch for this case-change when modifying to allow cell references in different worksheets...
3333
                    //    Should only be applied to the actual cell column, not the worksheet name
3334
3335
                    //    If the last entry on the stack was a : operator, then we have a cell range reference
3336
                    $testPrevOp = $stack->last(1);
3337
                    if ($testPrevOp['value'] == ':') {
3338
                        //    If we have a worksheet reference, then we're playing with a 3D reference
3339
                        if ($matches[2] == '') {
3340
                            //    Otherwise, we 'inherit' the worksheet reference from the start cell reference
3341
                            //    The start of the cell range reference should be the last entry in $output
3342
                            $startCellRef = $output[count($output)-1]['value'];
3343
                            preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches);
3344
                            if ($startMatches[2] > '') {
3345
                                $val = $startMatches[2].'!'.$val;
3346
                            }
3347
                        } else {
3348
                            return $this->raiseFormulaError("3D Range references are not yet supported");
3349
                        }
3350
                    }
3351
3352
                    $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val);
3353
//                    $expectingOperator = FALSE;
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...
3354
                } else {    // it's a variable, constant, string, number or boolean
3355
//                    echo 'Element is a Variable, Constant, String, Number or Boolean<br />';
3356
                    //    If the last entry on the stack was a : operator, then we may have a row or column range reference
3357
                    $testPrevOp = $stack->last(1);
3358
                    if ($testPrevOp['value'] == ':') {
3359
                        $startRowColRef = $output[count($output)-1]['value'];
3360
                        $rangeWS1 = '';
3361
                        if (strpos('!', $startRowColRef) !== false) {
3362
                            list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef);
3363
                        }
3364
                        if ($rangeWS1 != '') {
3365
                            $rangeWS1 .= '!';
3366
                        }
3367
                        $rangeWS2 = $rangeWS1;
3368
                        if (strpos('!', $val) !== false) {
3369
                            list($rangeWS2, $val) = explode('!', $val);
3370
                        }
3371
                        if ($rangeWS2 != '') {
3372
                            $rangeWS2 .= '!';
3373
                        }
3374
                        if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
3375
                            ($startRowColRef <= 1048576) && ($val <= 1048576)) {
3376
                            //    Row range
3377
                            $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD';    //    Max 16,384 columns for Excel2007
3378
                            $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef;
3379
                            $val = $rangeWS2.$endRowColRef.$val;
3380
                        } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
3381
                            (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
3382
                            //    Column range
3383
                            $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...
3384
                            $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1';
3385
                            $val = $rangeWS2.$val.$endRowColRef;
3386
                        }
3387
                    }
3388
3389
                    $localeConstant = false;
3390
                    if ($opCharacter == '"') {
3391
//                        echo 'Element is a String<br />';
3392
                        //    UnEscape any quotes within the string
3393
                        $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val)));
3394
                    } elseif (is_numeric($val)) {
3395
//                        echo 'Element is a Number<br />';
3396
                        if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
3397
//                            echo 'Casting '.$val.' to float<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3398
                            $val = (float) $val;
3399
                        } else {
3400
//                            echo 'Casting '.$val.' to integer<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3401
                            $val = (integer) $val;
3402
                        }
3403
                    } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
3404
                        $excelConstant = trim(strtoupper($val));
3405
//                        echo 'Element '.$excelConstant.' is an Excel Constant<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3406
                        $val = self::$excelConstants[$excelConstant];
3407
                    } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
3408
//                        echo 'Element '.$localeConstant.' is an Excel Constant<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3409
                        $val = self::$excelConstants[$localeConstant];
3410
                    }
3411
                    $details = array('type' => 'Value', 'value' => $val, 'reference' => null);
3412
                    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...
3413
                        $details['localeValue'] = $localeConstant;
3414
                    }
3415
                    $output[] = $details;
3416
                }
3417
                $index += $length;
3418
            } elseif ($opCharacter == '$') {    // absolute row or column range
3419
                ++$index;
3420
            } elseif ($opCharacter == ')') {    // miscellaneous error checking
3421
                if ($expectingOperand) {
3422
                    $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
3423
                    $expectingOperand = false;
3424
                    $expectingOperator = true;
3425
                } else {
3426
                    return $this->raiseFormulaError("Formula Error: Unexpected ')'");
3427
                }
3428
            } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
3429
                return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
3430
            } else {    // I don't even want to know what you did to get here
3431
                return $this->raiseFormulaError("Formula Error: An unexpected error occured");
3432
            }
3433
            //    Test for end of formula string
3434
            if ($index == strlen($formula)) {
3435
                //    Did we end with an operator?.
3436
                //    Only valid for the % unary operator
3437
                if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
3438
                    return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
3439
                } else {
3440
                    break;
3441
                }
3442
            }
3443
            //    Ignore white space
3444
            while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
3445
                ++$index;
3446
            }
3447
            if ($formula{$index} == ' ') {
3448
                while ($formula{$index} == ' ') {
3449
                    ++$index;
3450
                }
3451
                //    If we're expecting an operator, but only have a space between the previous and next operands (and both are
3452
                //        Cell References) then we have an INTERSECTION operator
3453
//                echo 'Possible Intersect Operator<br />';
3454
                if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) &&
3455
                    ($output[count($output)-1]['type'] == 'Cell Reference')) {
3456
//                    echo 'Element is an Intersect Operator<br />';
3457 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...
3458
                        ($o2 = $stack->last()) &&
3459
                        isset(self::$operators[$o2['value']]) &&
3460
                        @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3461
                        $output[] = $stack->pop();                                //    Swap operands and higher precedence operators from the stack to the output
3462
                    }
3463
                    $stack->push('Binary Operator', '|');    //    Put an Intersect Operator on the stack
3464
                    $expectingOperator = false;
3465
                }
3466
            }
3467
        }
3468
3469 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...
3470
            if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
3471
                return $this->raiseFormulaError("Formula Error: Expecting ')'");    // if there are any opening braces on the stack, then braces were unbalanced
3472
            }
3473
            $output[] = $op;
3474
        }
3475
        return $output;
3476
    }
3477
3478
3479
    private static function dataTestReference(&$operandData)
3480
    {
3481
        $operand = $operandData['value'];
3482
        if (($operandData['reference'] === null) && (is_array($operand))) {
3483
            $rKeys = array_keys($operand);
3484
            $rowKey = array_shift($rKeys);
3485
            $cKeys = array_keys(array_keys($operand[$rowKey]));
3486
            $colKey = array_shift($cKeys);
3487
            if (ctype_upper($colKey)) {
3488
                $operandData['reference'] = $colKey.$rowKey;
3489
            }
3490
        }
3491
        return $operand;
3492
    }
3493
3494
    // evaluate postfix notation
3495
    private function processTokenStack($tokens, $cellID = null, Cell $pCell = null)
3496
    {
3497
        if ($tokens == false) {
3498
            return false;
3499
        }
3500
3501
        //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
3502
        //        so we store the parent cell collection so that we can re-attach it when necessary
3503
        $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
3504
        $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
3505
        $stack = new Calculation\Token\Stack;
3506
3507
        //    Loop through each token in turn
3508
        foreach ($tokens as $tokenData) {
3509
//            print_r($tokenData);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
3510
//            echo '<br />';
3511
            $token = $tokenData['value'];
3512
//            echo '<b>Token is '.$token.'</b><br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3513
            // 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
3514
            if (isset(self::$binaryOperators[$token])) {
3515
//                echo 'Token is a binary operator<br />';
3516
                //    We must have two operands, error if we don't
3517
                if (($operand2Data = $stack->pop()) === null) {
3518
                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3519
                }
3520
                if (($operand1Data = $stack->pop()) === null) {
3521
                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3522
                }
3523
3524
                $operand1 = self::dataTestReference($operand1Data);
3525
                $operand2 = self::dataTestReference($operand2Data);
3526
3527
                //    Log what we're doing
3528
                if ($token == ':') {
3529
                    $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...
3530
                } else {
3531
                    $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...
3532
                }
3533
3534
                //    Process the operation in the appropriate manner
3535
                switch ($token) {
3536
                    //    Comparison (Boolean) Operators
3537
                    case '>':            //    Greater than
3538
                    case '<':            //    Less than
3539
                    case '>=':            //    Greater than or Equal to
3540
                    case '<=':            //    Less than or Equal to
3541
                    case '=':            //    Equality
3542
                    case '<>':            //    Inequality
3543
                        $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
3544
                        break;
3545
                    //    Binary Operators
3546
                    case ':':            //    Range
3547
                        $sheet1 = $sheet2 = '';
3548
                        if (strpos($operand1Data['reference'], '!') !== false) {
3549
                            list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']);
3550
                        } else {
3551
                            $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
3552
                        }
3553
                        if (strpos($operand2Data['reference'], '!') !== false) {
3554
                            list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']);
3555
                        } else {
3556
                            $sheet2 = $sheet1;
3557
                        }
3558
                        if ($sheet1 == $sheet2) {
3559 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...
3560
                                if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
3561
                                    $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...
3562
                                } elseif (trim($operand1Data['reference']) == '') {
3563
                                    $operand1Data['reference'] = $pCell->getCoordinate();
3564
                                } else {
3565
                                    $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
3566
                                }
3567
                            }
3568 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...
3569
                                if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
3570
                                    $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
3571
                                } elseif (trim($operand2Data['reference']) == '') {
3572
                                    $operand2Data['reference'] = $pCell->getCoordinate();
3573
                                } else {
3574
                                    $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
3575
                                }
3576
                            }
3577
3578
                            $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
3579
                            $oCol = $oRow = array();
3580
                            foreach ($oData as $oDatum) {
3581
                                $oCR = Cell::coordinateFromString($oDatum);
3582
                                $oCol[] = Cell::columnIndexFromString($oCR[0]) - 1;
3583
                                $oRow[] = $oCR[1];
3584
                            }
3585
                            $cellRef = Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.Cell::stringFromColumnIndex(max($oCol)).max($oRow);
3586 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...
3587
                                $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false);
3588
                            } else {
3589
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3590
                            }
3591
                            $stack->push('Cell Reference', $cellValue, $cellRef);
3592
                        } else {
3593
                            $stack->push('Error', Calculation\Functions::REF(), null);
3594
                        }
3595
                        break;
3596
                    case '+':            //    Addition
3597
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack);
3598
                        break;
3599
                    case '-':            //    Subtraction
3600
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack);
3601
                        break;
3602
                    case '*':            //    Multiplication
3603
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack);
3604
                        break;
3605
                    case '/':            //    Division
3606
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack);
3607
                        break;
3608
                    case '^':            //    Exponential
3609
                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack);
3610
                        break;
3611
                    case '&':            //    Concatenation
3612
                        //    If either of the operands is a matrix, we need to treat them both as matrices
3613
                        //        (converting the other operand to a matrix if need be); then perform the required
3614
                        //        matrix operation
3615
                        if (is_bool($operand1)) {
3616
                            $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3617
                        }
3618
                        if (is_bool($operand2)) {
3619
                            $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3620
                        }
3621
                        if ((is_array($operand1)) || (is_array($operand2))) {
3622
                            //    Ensure that both operands are arrays/matrices
3623
                            self::checkMatrixOperands($operand1, $operand2, 2);
3624
                            try {
3625
                                //    Convert operand 1 from a PHP array to a matrix
3626
                                $matrix = new Shared\JAMA\Matrix($operand1);
3627
                                //    Perform the required operation against the operand 1 matrix, passing in operand 2
3628
                                $matrixResult = $matrix->concat($operand2);
3629
                                $result = $matrixResult->getArray();
3630
                            } catch (Exception $ex) {
3631
                                $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...
3632
                                $result = '#VALUE!';
3633
                            }
3634
                        } else {
3635
                            $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...
3636
                        }
3637
                        $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...
3638
                        $stack->push('Value', $result);
3639
                        break;
3640
                    case '|':            //    Intersect
3641
                        $rowIntersect = array_intersect_key($operand1, $operand2);
3642
                        $cellIntersect = $oCol = $oRow = array();
3643
                        foreach (array_keys($rowIntersect) as $row) {
3644
                            $oRow[] = $row;
3645
                            foreach ($rowIntersect[$row] as $col => $data) {
3646
                                $oCol[] = Cell::columnIndexFromString($col) - 1;
3647
                                $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
3648
                            }
3649
                        }
3650
                        $cellRef = Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.Cell::stringFromColumnIndex(max($oCol)).max($oRow);
3651
                        $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...
3652
                        $stack->push('Value', $cellIntersect, $cellRef);
3653
                        break;
3654
                }
3655
3656
            // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
3657
            } elseif (($token === '~') || ($token === '%')) {
3658
//                echo 'Token is a unary operator<br />';
3659
                if (($arg = $stack->pop()) === null) {
3660
                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3661
                }
3662
                $arg = $arg['value'];
3663
                if ($token === '~') {
3664
//                    echo 'Token is a negation operator<br />';
3665
                    $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...
3666
                    $multiplier = -1;
3667
                } else {
3668
//                    echo 'Token is a percentile operator<br />';
3669
                    $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...
3670
                    $multiplier = 0.01;
3671
                }
3672
                if (is_array($arg)) {
3673
                    self::checkMatrixOperands($arg, $multiplier, 2);
3674
                    try {
3675
                        $matrix1 = new Shared\JAMA\Matrix($arg);
3676
                        $matrixResult = $matrix1->arrayTimesEquals($multiplier);
3677
                        $result = $matrixResult->getArray();
3678
                    } catch (Exception $ex) {
3679
                        $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...
3680
                        $result = '#VALUE!';
3681
                    }
3682
                    $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...
3683
                    $stack->push('Value', $result);
3684
                } else {
3685
                    $this->executeNumericBinaryOperation($cellID, $multiplier, $arg, '*', 'arrayTimesEquals', $stack);
3686
                }
3687
            } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
3688
                $cellRef = null;
3689
//                echo 'Element '.$token.' is a Cell reference<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3690
                if (isset($matches[8])) {
3691
//                    echo 'Reference is a Range of cells<br />';
3692
                    if ($pCell === null) {
3693
//                        We can't access the range, so return a REF error
3694
                        $cellValue = Calculation\Functions::REF();
3695
                    } else {
3696
                        $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
3697
                        if ($matches[2] > '') {
3698
                            $matches[2] = trim($matches[2], "\"'");
3699 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...
3700
                                //    It's a Reference to an external spreadsheet (not currently supported)
3701
                                return $this->raiseFormulaError('Unable to access External Workbook');
3702
                            }
3703
                            $matches[2] = trim($matches[2], "\"'");
3704
//                            echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
3705
                            $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...
3706 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...
3707
                                $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
3708
                            } else {
3709
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3710
                            }
3711
                            $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...
3712
//                            $cellRef = $matches[2].'!'.$cellRef;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
3713
                        } else {
3714
//                            echo '$cellRef='.$cellRef.' in current worksheet<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3715
                            $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...
3716
                            if ($pCellParent !== null) {
3717
                                $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3718
                            } else {
3719
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3720
                            }
3721
                            $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...
3722
                        }
3723
                    }
3724
                } else {
3725
//                    echo 'Reference is a single Cell<br />';
3726
                    if ($pCell === null) {
3727
//                        We can't access the cell, so return a REF error
3728
                        $cellValue = Calculation\Functions::REF();
3729
                    } else {
3730
                        $cellRef = $matches[6].$matches[7];
3731
                        if ($matches[2] > '') {
3732
                            $matches[2] = trim($matches[2], "\"'");
3733 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...
3734
                                //    It's a Reference to an external spreadsheet (not currently supported)
3735
                                return $this->raiseFormulaError('Unable to access External Workbook');
3736
                            }
3737
//                            echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
3738
                            $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...
3739
                            if ($pCellParent !== null) {
3740
                                $cellSheet = $this->spreadsheet->getSheetByName($matches[2]);
3741
                                if ($cellSheet && $cellSheet->cellExists($cellRef)) {
3742
                                    $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
3743
                                    $pCell->attach($pCellParent);
3744
                                } else {
3745
                                    $cellValue = null;
3746
                                }
3747
                            } else {
3748
                                return $this->raiseFormulaError('Unable to access Cell Reference');
3749
                            }
3750
                            $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...
3751
//                            $cellRef = $matches[2].'!'.$cellRef;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
3752
                        } else {
3753
//                            echo '$cellRef='.$cellRef.' in current worksheet<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3754
                            $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...
3755
                            if ($pCellParent->isDataSet($cellRef)) {
3756
                                $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3757
                                $pCell->attach($pCellParent);
0 ignored issues
show
Bug introduced by
It seems like $pCellParent defined by $pCell !== null ? $pCell->getParent() : null on line 3504 can be null; however, 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...
3758
                            } else {
3759
                                $cellValue = null;
3760
                            }
3761
                            $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...
3762
                        }
3763
                    }
3764
                }
3765
                $stack->push('Value', $cellValue, $cellRef);
3766
3767
            // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
3768
            } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
3769
//                echo 'Token is a function<br />';
3770
                $functionName = $matches[1];
3771
                $argCount = $stack->pop();
3772
                $argCount = $argCount['value'];
3773
                if ($functionName != 'MKMATRIX') {
3774
                    $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...
3775
                }
3776
                if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) {    // function
3777
                    if (isset(self::$phpSpreadsheetFunctions[$functionName])) {
3778
                        $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
3779
                        $passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']);
3780
                        $passCellReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passCellReference']);
3781
                    } elseif (isset(self::$controlFunctions[$functionName])) {
3782
                        $functionCall = self::$controlFunctions[$functionName]['functionCall'];
3783
                        $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
3784
                        $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
3785
                    }
3786
                    // get the arguments for this function
3787
//                    echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />';
3788
                    $args = $argArrayVals = array();
3789
                    for ($i = 0; $i < $argCount; ++$i) {
3790
                        $arg = $stack->pop();
3791
                        $a = $argCount - $i - 1;
3792
                        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...
3793
                            (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) &&
3794
                            (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) {
3795
                            if ($arg['reference'] === null) {
3796
                                $args[] = $cellID;
3797
                                if ($functionName != 'MKMATRIX') {
3798
                                    $argArrayVals[] = $this->showValue($cellID);
3799
                                }
3800
                            } else {
3801
                                $args[] = $arg['reference'];
3802
                                if ($functionName != 'MKMATRIX') {
3803
                                    $argArrayVals[] = $this->showValue($arg['reference']);
3804
                                }
3805
                            }
3806
                        } else {
3807
                            $args[] = self::unwrapResult($arg['value']);
3808
                            if ($functionName != 'MKMATRIX') {
3809
                                $argArrayVals[] = $this->showValue($arg['value']);
3810
                            }
3811
                        }
3812
                    }
3813
                    //    Reverse the order of the arguments
3814
                    krsort($args);
3815
                    if (($passByReference) && ($argCount == 0)) {
3816
                        $args[] = $cellID;
3817
                        $argArrayVals[] = $this->showValue($cellID);
3818
                    }
3819
//                    echo 'Arguments are: ';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3820
//                    print_r($args);
3821
//                    echo '<br />';
3822
                    if ($functionName != 'MKMATRIX') {
3823
                        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...
3824
                            krsort($argArrayVals);
3825
                            $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...
3826
                        }
3827
                    }
3828
                    //    Process each argument in turn, building the return value as an array
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
3829
//                    if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
3830
//                        $operand1 = $args[1];
3831
//                        $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->showValue($operand1));
3832
//                        $result = array();
3833
//                        $row = 0;
3834
//                        foreach($operand1 as $args) {
3835
//                            if (is_array($args)) {
3836
//                                foreach($args as $arg) {
3837
//                                    $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($arg), ' )');
3838
//                                    $r = call_user_func_array($functionCall, $arg);
3839
//                                    $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
3840
//                                    $result[$row][] = $r;
3841
//                                }
3842
//                                ++$row;
3843
//                            } else {
3844
//                                $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($args), ' )');
3845
//                                $r = call_user_func_array($functionCall, $args);
3846
//                                $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
3847
//                                $result[] = $r;
3848
//                            }
3849
//                        }
3850
//                    } else {
3851
                    //    Process the argument with the appropriate function call
3852
                    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...
3853
                        $args[] = $pCell;
3854
                    }
3855
                    if (strpos($functionCall, '::') !== false) {
3856
                        $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...
3857
                    } else {
3858
                        foreach ($args as &$arg) {
3859
                            $arg = Calculation\Functions::flattenSingleValue($arg);
3860
                        }
3861
                        unset($arg);
3862
                        $result = call_user_func_array($functionCall, $args);
3863
                    }
3864
                    if ($functionName != 'MKMATRIX') {
3865
                        $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...
3866
                    }
3867
                    $stack->push('Value', self::wrapResult($result));
3868
                }
3869
            } else {
3870
                // if the token is a number, boolean, string or an Excel error, push it onto the stack
3871
                if (isset(self::$excelConstants[strtoupper($token)])) {
3872
                    $excelConstant = strtoupper($token);
3873
//                    echo 'Token is a PhpSpreadsheet constant: '.$excelConstant.'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3874
                    $stack->push('Constant Value', self::$excelConstants[$excelConstant]);
3875
                    $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...
3876
                } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
3877
//                    echo 'Token is a number, boolean, string, null or an Excel error<br />';
3878
                    $stack->push('Value', $token);
3879
                // if the token is a named range, push the named range name onto the stack
3880
                } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
3881
//                    echo 'Token is a named range<br />';
3882
                    $namedRange = $matches[6];
3883
//                    echo 'Named Range is '.$namedRange.'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3884
                    $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...
3885
                    $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false);
3886
                    $pCell->attach($pCellParent);
0 ignored issues
show
Bug introduced by
It seems like $pCellParent defined by $pCell !== null ? $pCell->getParent() : null on line 3504 can be null; however, 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...
3887
                    $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...
3888
                    $stack->push('Named Range', $cellValue, $namedRange);
3889
                } else {
3890
                    return $this->raiseFormulaError("undefined variable '$token'");
3891
                }
3892
            }
3893
        }
3894
        // when we're out of tokens, the stack should have a single element, the final result
3895
        if ($stack->count() != 1) {
3896
            return $this->raiseFormulaError("internal error");
3897
        }
3898
        $output = $stack->pop();
3899
        $output = $output['value'];
3900
3901
//        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...
3902
//            return array_shift(Calculation\Functions::flattenArray($output));
3903
//        }
3904
        return $output;
3905
    }
3906
3907
3908
    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...
3909
    {
3910
        if (is_array($operand)) {
3911
            if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
3912
                do {
3913
                    $operand = array_pop($operand);
3914
                } while (is_array($operand));
3915
            }
3916
        }
3917
        //    Numbers, matrices and booleans can pass straight through, as they're already valid
3918
        if (is_string($operand)) {
3919
            //    We only need special validations for the operand if it is a string
3920
            //    Start by stripping off the quotation marks we use to identify true excel string values internally
3921
            if ($operand > '' && $operand{0} == '"') {
3922
                $operand = self::unwrapResult($operand);
3923
            }
3924
            //    If the string is a numeric value, we treat it as a numeric, so no further testing
3925
            if (!is_numeric($operand)) {
3926
                //    If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
3927
                if ($operand > '' && $operand{0} == '#') {
3928
                    $stack->push('Value', $operand);
3929
                    $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...
3930
                    return false;
3931
                } elseif (!Shared\StringHelper::convertToNumberIfFraction($operand)) {
3932
                    //    If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
3933
                    $stack->push('Value', '#VALUE!');
3934
                    $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...
3935
                    return false;
3936
                }
3937
            }
3938
        }
3939
3940
        //    return a true if the value of the operand is one that we can use in normal binary operations
3941
        return true;
3942
    }
3943
3944
3945
    private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays = false)
3946
    {
3947
        //    If we're dealing with matrix operations, we want a matrix result
3948
        if ((is_array($operand1)) || (is_array($operand2))) {
3949
            $result = array();
3950
            if ((is_array($operand1)) && (!is_array($operand2))) {
3951 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...
3952
                    $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...
3953
                    $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
3954
                    $r = $stack->pop();
3955
                    $result[$x] = $r['value'];
3956
                }
3957
            } elseif ((!is_array($operand1)) && (is_array($operand2))) {
3958 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...
3959
                    $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...
3960
                    $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
3961
                    $r = $stack->pop();
3962
                    $result[$x] = $r['value'];
3963
                }
3964
            } else {
3965
                if (!$recursingArrays) {
3966
                    self::checkMatrixOperands($operand1, $operand2, 2);
3967
                }
3968
                foreach ($operand1 as $x => $operandData) {
3969
                    $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...
3970
                    $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
3971
                    $r = $stack->pop();
3972
                    $result[$x] = $r['value'];
3973
                }
3974
            }
3975
            //    Log the result details
3976
            $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...
3977
            //    And push the result onto the stack
3978
            $stack->push('Array', $result);
3979
            return true;
3980
        }
3981
3982
        //    Simple validate the two operands if they are string values
3983
        if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') {
3984
            $operand1 = self::unwrapResult($operand1);
3985
        }
3986
        if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') {
3987
            $operand2 = self::unwrapResult($operand2);
3988
        }
3989
3990
        // Use case insensitive comparaison if not OpenOffice mode
3991
        if (Calculation\Functions::getCompatibilityMode() != Calculation\Functions::COMPATIBILITY_OPENOFFICE) {
3992
            if (is_string($operand1)) {
3993
                $operand1 = strtoupper($operand1);
3994
            }
3995
            if (is_string($operand2)) {
3996
                $operand2 = strtoupper($operand2);
3997
            }
3998
        }
3999
4000
        $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && Calculation\Functions::getCompatibilityMode() == Calculation\Functions::COMPATIBILITY_OPENOFFICE;
4001
4002
        //    execute the necessary operation
4003
        switch ($operation) {
4004
            //    Greater than
4005 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...
4006
                if ($useLowercaseFirstComparison) {
4007
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
4008
                } else {
4009
                    $result = ($operand1 > $operand2);
4010
                }
4011
                break;
4012
            //    Less than
4013 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...
4014
                if ($useLowercaseFirstComparison) {
4015
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
4016
                } else {
4017
                    $result = ($operand1 < $operand2);
4018
                }
4019
                break;
4020
            //    Equality
4021 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...
4022
                if (is_numeric($operand1) && is_numeric($operand2)) {
4023
                    $result = (abs($operand1 - $operand2) < $this->delta);
4024
                } else {
4025
                    $result = strcmp($operand1, $operand2) == 0;
4026
                }
4027
                break;
4028
            //    Greater than or equal
4029
            case '>=':
4030
                if (is_numeric($operand1) && is_numeric($operand2)) {
4031
                    $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
4032
                } elseif ($useLowercaseFirstComparison) {
4033
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
4034
                } else {
4035
                    $result = strcmp($operand1, $operand2) >= 0;
4036
                }
4037
                break;
4038
            //    Less than or equal
4039
            case '<=':
4040
                if (is_numeric($operand1) && is_numeric($operand2)) {
4041
                    $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
4042
                } elseif ($useLowercaseFirstComparison) {
4043
                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
4044
                } else {
4045
                    $result = strcmp($operand1, $operand2) <= 0;
4046
                }
4047
                break;
4048
            //    Inequality
4049 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...
4050
                if (is_numeric($operand1) && is_numeric($operand2)) {
4051
                    $result = (abs($operand1 - $operand2) > 1E-14);
4052
                } else {
4053
                    $result = strcmp($operand1, $operand2) != 0;
4054
                }
4055
                break;
4056
        }
4057
4058
        //    Log the result details
4059
        $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...
4060
        //    And push the result onto the stack
4061
        $stack->push('Value', $result);
4062
        return true;
4063
    }
4064
4065
    /**
4066
     * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters
4067
     * @param    string    $str1    First string value for the comparison
4068
     * @param    string    $str2    Second string value for the comparison
4069
     * @return   integer
4070
     */
4071
    private function strcmpLowercaseFirst($str1, $str2)
4072
    {
4073
        $inversedStr1 = Shared\StringHelper::strCaseReverse($str1);
4074
        $inversedStr2 = Shared\StringHelper::strCaseReverse($str2);
4075
4076
        return strcmp($inversedStr1, $inversedStr2);
4077
    }
4078
4079
    private function executeNumericBinaryOperation($cellID, $operand1, $operand2, $operation, $matrixFunction, &$stack)
4080
    {
4081
        //    Validate the two operands
4082
        if (!$this->validateBinaryOperand($cellID, $operand1, $stack)) {
4083
            return false;
4084
        }
4085
        if (!$this->validateBinaryOperand($cellID, $operand2, $stack)) {
4086
            return false;
4087
        }
4088
4089
        //    If either of the operands is a matrix, we need to treat them both as matrices
4090
        //        (converting the other operand to a matrix if need be); then perform the required
4091
        //        matrix operation
4092
        if ((is_array($operand1)) || (is_array($operand2))) {
4093
            //    Ensure that both operands are arrays/matrices of the same size
4094
            self::checkMatrixOperands($operand1, $operand2, 2);
4095
4096
            try {
4097
                //    Convert operand 1 from a PHP array to a matrix
4098
                $matrix = new Shared\JAMA\Matrix($operand1);
4099
                //    Perform the required operation against the operand 1 matrix, passing in operand 2
4100
                $matrixResult = $matrix->$matrixFunction($operand2);
4101
                $result = $matrixResult->getArray();
4102
            } catch (Exception $ex) {
4103
                $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...
4104
                $result = '#VALUE!';
4105
            }
4106
        } else {
4107
            if ((Calculation\Functions::getCompatibilityMode() != Calculation\Functions::COMPATIBILITY_OPENOFFICE) &&
4108
                ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1)>0) ||
4109
                 (is_string($operand2) && !is_numeric($operand2) && strlen($operand2)>0))) {
4110
                $result = Calculation\Functions::VALUE();
4111
            } else {
4112
                //    If we're dealing with non-matrix operations, execute the necessary operation
4113
                switch ($operation) {
4114
                    //    Addition
4115
                    case '+':
4116
                        $result = $operand1 + $operand2;
4117
                        break;
4118
                    //    Subtraction
4119
                    case '-':
4120
                        $result = $operand1 - $operand2;
4121
                        break;
4122
                    //    Multiplication
4123
                    case '*':
4124
                        $result = $operand1 * $operand2;
4125
                        break;
4126
                    //    Division
4127
                    case '/':
4128
                        if ($operand2 == 0) {
4129
                            //    Trap for Divide by Zero error
4130
                            $stack->push('Value', '#DIV/0!');
4131
                            $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...
4132
                            return false;
4133
                        } else {
4134
                            $result = $operand1 / $operand2;
4135
                        }
4136
                        break;
4137
                    //    Power
4138
                    case '^':
4139
                        $result = pow($operand1, $operand2);
4140
                        break;
4141
                }
4142
            }
4143
        }
4144
4145
        //    Log the result details
4146
        $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...
4147
        //    And push the result onto the stack
4148
        $stack->push('Value', $result);
4149
        return true;
4150
    }
4151
4152
4153
    // trigger an error, but nicely, if need be
4154
    protected function raiseFormulaError($errorMessage)
4155
    {
4156
        $this->formulaError = $errorMessage;
4157
        $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...
4158
        if (!$this->suppressFormulaErrors) {
4159
            throw new Calculation\Exception($errorMessage);
4160
        }
4161
        trigger_error($errorMessage, E_USER_ERROR);
4162
    }
4163
4164
4165
    /**
4166
     * Extract range values
4167
     *
4168
     * @param    string      &$pRange    String based range representation
4169
     * @param    Worksheet   $pSheet        Worksheet
4170
     * @param    boolean     $resetLog    Flag indicating whether calculation log should be reset or not
4171
     * @return   mixed       Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4172
     * @throws   Calculation\Exception
4173
     */
4174
    public function extractCellRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true)
4175
    {
4176
        // Return value
4177
        $returnValue = array ();
4178
4179
//        echo 'extractCellRange('.$pRange.')', PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4180
        if ($pSheet !== null) {
4181
            $pSheetName = $pSheet->getTitle();
4182
//            echo 'Passed sheet name is '.$pSheetName.PHP_EOL;
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...
4183
//            echo 'Range reference is '.$pRange.PHP_EOL;
4184 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...
4185
//                echo '$pRange reference includes sheet reference', PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4186
                list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
4187
//                echo 'New sheet name is '.$pSheetName, PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% 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...
4188
//                echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
4189
                $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
4190
            }
4191
4192
            // Extract range
4193
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
4194
            $pRange = $pSheetName.'!'.$pRange;
4195
            if (!isset($aReferences[1])) {
4196
                //    Single cell in range
4197
                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...
4198
                $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...
4199 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...
4200
                    $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4201
                } else {
4202
                    $returnValue[$currentRow][$currentCol] = null;
4203
                }
4204
            } else {
4205
                // Extract cell data for all cells in the range
4206
                foreach ($aReferences as $reference) {
4207
                    // Extract range
4208
                    sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
4209
                    $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...
4210 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...
4211
                        $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4212
                    } else {
4213
                        $returnValue[$currentRow][$currentCol] = null;
4214
                    }
4215
                }
4216
            }
4217
        }
4218
4219
        return $returnValue;
4220
    }
4221
4222
4223
    /**
4224
     * Extract range values
4225
     *
4226
     * @param    string       &$pRange    String based range representation
4227
     * @param    Worksheet    $pSheet        Worksheet
4228
     * @return   mixed        Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4229
     * @param    boolean      $resetLog    Flag indicating whether calculation log should be reset or not
4230
     * @throws   Calculation\Exception
4231
     */
4232
    public function extractNamedRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true)
4233
    {
4234
        // Return value
4235
        $returnValue = array ();
4236
4237
//        echo 'extractNamedRange('.$pRange.')<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
4238
        if ($pSheet !== null) {
4239
            $pSheetName = $pSheet->getTitle();
4240
//            echo 'Current sheet name is '.$pSheetName.'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% 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...
4241
//            echo 'Range reference is '.$pRange.'<br />';
4242 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...
4243
//                echo '$pRange reference includes sheet reference', PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4244
                list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
4245
//                echo 'New sheet name is '.$pSheetName, PHP_EOL;
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% 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...
4246
//                echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
4247
                $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
4248
            }
4249
4250
            // Named range?
4251
            $namedRange = NamedRange::resolveRange($pRange, $pSheet);
4252
            if ($namedRange !== null) {
4253
                $pSheet = $namedRange->getWorksheet();
4254
//                echo 'Named Range '.$pRange.' (';
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
4255
                $pRange = $namedRange->getRange();
4256
                $splitRange = Cell::splitRange($pRange);
4257
                //    Convert row and column references
4258
                if (ctype_alpha($splitRange[0][0])) {
4259
                    $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
4260
                } elseif (ctype_digit($splitRange[0][0])) {
4261
                    $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
4262
                }
4263
//                echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% 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...
4264
4265
//                if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
4266
//                    if (!$namedRange->getLocalOnly()) {
4267
//                        $pSheet = $namedRange->getWorksheet();
4268
//                    } else {
4269
//                        return $returnValue;
4270
//                    }
4271
//                }
4272
            } else {
4273
                return Calculation\Functions::REF();
4274
            }
4275
4276
            // Extract range
4277
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
4278
//            var_dump($aReferences);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4279
            if (!isset($aReferences[1])) {
4280
                //    Single cell (or single column or row) in range
4281
                list($currentCol, $currentRow) = Cell::coordinateFromString($aReferences[0]);
4282
                $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...
4283 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...
4284
                    $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4285
                } else {
4286
                    $returnValue[$currentRow][$currentCol] = null;
4287
                }
4288
            } else {
4289
                // Extract cell data for all cells in the range
4290
                foreach ($aReferences as $reference) {
4291
                    // Extract range
4292
                    list($currentCol, $currentRow) = Cell::coordinateFromString($reference);
4293
//                    echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'<br />';
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
4294
                    $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...
4295 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...
4296
                        $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4297
                    } else {
4298
                        $returnValue[$currentRow][$currentCol] = null;
4299
                    }
4300
                }
4301
            }
4302
//                print_r($returnValue);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
4303
//            echo '<br />';
4304
        }
4305
4306
        return $returnValue;
4307
    }
4308
4309
4310
    /**
4311
     * Is a specific function implemented?
4312
     *
4313
     * @param    string    $pFunction    Function Name
4314
     * @return    boolean
4315
     */
4316
    public function isImplemented($pFunction = '')
4317
    {
4318
        $pFunction = strtoupper($pFunction);
4319
        if (isset(self::$phpSpreadsheetFunctions[$pFunction])) {
4320
            return (self::$phpSpreadsheetFunctions[$pFunction]['functionCall'] != 'Calculation\Categories::DUMMY');
4321
        } else {
4322
            return false;
4323
        }
4324
    }
4325
4326
4327
    /**
4328
     * Get a list of all implemented functions as an array of function objects
4329
     *
4330
     * @return    array of Calculation\Categories
4331
     */
4332
    public function listFunctions()
4333
    {
4334
        $returnValue = array();
4335
4336
        foreach (self::$phpSpreadsheetFunctions as $functionName => $function) {
4337
            if ($function['functionCall'] != 'Calculation\Categories::DUMMY') {
4338
                $returnValue[$functionName] = new Calculation\Categories(
4339
                    $function['category'],
4340
                    $functionName,
4341
                    $function['functionCall']
4342
                );
4343
            }
4344
        }
4345
4346
        return $returnValue;
4347
    }
4348
4349
4350
    /**
4351
     * Get a list of all Excel function names
4352
     *
4353
     * @return    array
4354
     */
4355
    public function listAllFunctionNames()
4356
    {
4357
        return array_keys(self::$phpSpreadsheetFunctions);
4358
    }
4359
4360
    /**
4361
     * Get a list of implemented Excel function names
4362
     *
4363
     * @return    array
4364
     */
4365
    public function listFunctionNames()
4366
    {
4367
        $returnValue = array();
4368
        foreach (self::$phpSpreadsheetFunctions as $functionName => $function) {
4369
            if ($function['functionCall'] != 'Calculation\Categories::DUMMY') {
4370
                $returnValue[] = $functionName;
4371
            }
4372
        }
4373
4374
        return $returnValue;
4375
    }
4376
}
4377