Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Calculation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Calculation, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class Calculation |
||
15 | { |
||
16 | /** Constants */ |
||
17 | /** Regular Expressions */ |
||
18 | // Numeric operand |
||
19 | const CALCULATION_REGEXP_NUMBER = '[-+]?\d*\.?\d+(e[-+]?\d+)?'; |
||
20 | // String operand |
||
21 | const CALCULATION_REGEXP_STRING = '"(?:[^"]|"")*"'; |
||
22 | // Opening bracket |
||
23 | const CALCULATION_REGEXP_OPENBRACE = '\('; |
||
24 | // Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it) |
||
25 | const CALCULATION_REGEXP_FUNCTION = '@?([A-Z][A-Z0-9\.]*)[\s]*\('; |
||
26 | // Cell reference (cell or range of cells, with or without a sheet reference) |
||
27 | const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})'; |
||
28 | // Named Range of cells |
||
29 | const CALCULATION_REGEXP_NAMEDRANGE = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)'; |
||
30 | // Error |
||
31 | const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?'; |
||
32 | |||
33 | /** constants */ |
||
34 | const RETURN_ARRAY_AS_ERROR = 'error'; |
||
35 | const RETURN_ARRAY_AS_VALUE = 'value'; |
||
36 | const RETURN_ARRAY_AS_ARRAY = 'array'; |
||
37 | |||
38 | private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE; |
||
39 | |||
40 | /** |
||
41 | * Instance of this class. |
||
42 | * |
||
43 | * @var \PhpOffice\PhpSpreadsheet\Calculation |
||
44 | */ |
||
45 | private static $instance; |
||
46 | |||
47 | /** |
||
48 | * Instance of the spreadsheet this Calculation Engine is using. |
||
49 | * |
||
50 | * @var PhpSpreadsheet |
||
51 | */ |
||
52 | private $spreadsheet; |
||
53 | |||
54 | /** |
||
55 | * Calculation cache. |
||
56 | * |
||
57 | * @var array |
||
58 | */ |
||
59 | private $calculationCache = []; |
||
60 | |||
61 | /** |
||
62 | * Calculation cache enabled. |
||
63 | * |
||
64 | * @var bool |
||
65 | */ |
||
66 | private $calculationCacheEnabled = true; |
||
67 | |||
68 | /** |
||
69 | * List of operators that can be used within formulae |
||
70 | * The true/false value indicates whether it is a binary operator or a unary operator. |
||
71 | * |
||
72 | * @var array |
||
73 | */ |
||
74 | private static $operators = [ |
||
75 | '+' => true, '-' => true, '*' => true, '/' => true, |
||
76 | '^' => true, '&' => true, '%' => false, '~' => false, |
||
77 | '>' => true, '<' => true, '=' => true, '>=' => true, |
||
78 | '<=' => true, '<>' => true, '|' => true, ':' => true, |
||
79 | ]; |
||
80 | |||
81 | /** |
||
82 | * List of binary operators (those that expect two operands). |
||
83 | * |
||
84 | * @var array |
||
85 | */ |
||
86 | private static $binaryOperators = [ |
||
87 | '+' => true, '-' => true, '*' => true, '/' => true, |
||
88 | '^' => true, '&' => true, '>' => true, '<' => true, |
||
89 | '=' => true, '>=' => true, '<=' => true, '<>' => true, |
||
90 | '|' => true, ':' => true, |
||
91 | ]; |
||
92 | |||
93 | /** |
||
94 | * The debug log generated by the calculation engine. |
||
95 | * |
||
96 | * @var Logger |
||
97 | */ |
||
98 | private $debugLog; |
||
99 | |||
100 | /** |
||
101 | * Flag to determine how formula errors should be handled |
||
102 | * If true, then a user error will be triggered |
||
103 | * If false, then an exception will be thrown. |
||
104 | * |
||
105 | * @var bool |
||
106 | */ |
||
107 | public $suppressFormulaErrors = false; |
||
108 | |||
109 | /** |
||
110 | * Error message for any error that was raised/thrown by the calculation engine. |
||
111 | * |
||
112 | * @var string |
||
113 | */ |
||
114 | public $formulaError; |
||
115 | |||
116 | /** |
||
117 | * An array of the nested cell references accessed by the calculation engine, used for the debug log. |
||
118 | * |
||
119 | * @var array of string |
||
120 | */ |
||
121 | private $cyclicReferenceStack; |
||
122 | |||
123 | private $cellStack = []; |
||
124 | |||
125 | /** |
||
126 | * Current iteration counter for cyclic formulae |
||
127 | * If the value is 0 (or less) then cyclic formulae will throw an exception, |
||
128 | * otherwise they will iterate to the limit defined here before returning a result. |
||
129 | * |
||
130 | * @var int |
||
131 | */ |
||
132 | private $cyclicFormulaCounter = 1; |
||
133 | |||
134 | private $cyclicFormulaCell = ''; |
||
135 | |||
136 | /** |
||
137 | * Number of iterations for cyclic formulae. |
||
138 | * |
||
139 | * @var int |
||
140 | */ |
||
141 | public $cyclicFormulaCount = 1; |
||
142 | |||
143 | /** |
||
144 | * Epsilon Precision used for comparisons in calculations. |
||
145 | * |
||
146 | * @var float |
||
147 | */ |
||
148 | private $delta = 0.1e-12; |
||
149 | |||
150 | /** |
||
151 | * The current locale setting. |
||
152 | * |
||
153 | * @var string |
||
154 | */ |
||
155 | private static $localeLanguage = 'en_us'; // US English (default locale) |
||
156 | |||
157 | /** |
||
158 | * List of available locale settings |
||
159 | * Note that this is read for the locale subdirectory only when requested. |
||
160 | * |
||
161 | * @var string[] |
||
162 | */ |
||
163 | private static $validLocaleLanguages = [ |
||
164 | 'en', // English (default language) |
||
|
|||
165 | ]; |
||
166 | |||
167 | /** |
||
168 | * Locale-specific argument separator for function arguments. |
||
169 | * |
||
170 | * @var string |
||
171 | */ |
||
172 | private static $localeArgumentSeparator = ','; |
||
173 | private static $localeFunctions = []; |
||
174 | |||
175 | /** |
||
176 | * Locale-specific translations for Excel constants (True, False and Null). |
||
177 | * |
||
178 | * @var string[] |
||
179 | */ |
||
180 | public static $localeBoolean = [ |
||
181 | 'TRUE' => 'TRUE', |
||
182 | 'FALSE' => 'FALSE', |
||
183 | 'NULL' => 'NULL', |
||
184 | ]; |
||
185 | |||
186 | /** |
||
187 | * Excel constant string translations to their PHP equivalents |
||
188 | * Constant conversion from text name/value to actual (datatyped) value. |
||
189 | * |
||
190 | * @var string[] |
||
191 | */ |
||
192 | private static $excelConstants = [ |
||
193 | 'TRUE' => true, |
||
194 | 'FALSE' => false, |
||
195 | 'NULL' => null, |
||
196 | ]; |
||
197 | |||
198 | // PhpSpreadsheet functions |
||
199 | private static $phpSpreadsheetFunctions = [ |
||
200 | 'ABS' => [ |
||
201 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
202 | 'functionCall' => 'abs', |
||
203 | 'argumentCount' => '1', |
||
204 | ], |
||
205 | 'ACCRINT' => [ |
||
206 | 'category' => Category::CATEGORY_FINANCIAL, |
||
207 | 'functionCall' => [Financial::class, 'ACCRINT'], |
||
208 | 'argumentCount' => '4-7', |
||
209 | ], |
||
210 | 'ACCRINTM' => [ |
||
211 | 'category' => Category::CATEGORY_FINANCIAL, |
||
212 | 'functionCall' => [Financial::class, 'ACCRINTM'], |
||
213 | 'argumentCount' => '3-5', |
||
214 | ], |
||
215 | 'ACOS' => [ |
||
216 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
217 | 'functionCall' => 'acos', |
||
218 | 'argumentCount' => '1', |
||
219 | ], |
||
220 | 'ACOSH' => [ |
||
221 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
222 | 'functionCall' => 'acosh', |
||
223 | 'argumentCount' => '1', |
||
224 | ], |
||
225 | 'ADDRESS' => [ |
||
226 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
227 | 'functionCall' => [LookupRef::class, 'cellAddress'], |
||
228 | 'argumentCount' => '2-5', |
||
229 | ], |
||
230 | 'AMORDEGRC' => [ |
||
231 | 'category' => Category::CATEGORY_FINANCIAL, |
||
232 | 'functionCall' => [Financial::class, 'AMORDEGRC'], |
||
233 | 'argumentCount' => '6,7', |
||
234 | ], |
||
235 | 'AMORLINC' => [ |
||
236 | 'category' => Category::CATEGORY_FINANCIAL, |
||
237 | 'functionCall' => [Financial::class, 'AMORLINC'], |
||
238 | 'argumentCount' => '6,7', |
||
239 | ], |
||
240 | 'AND' => [ |
||
241 | 'category' => Category::CATEGORY_LOGICAL, |
||
242 | 'functionCall' => [Logical::class, 'logicalAnd'], |
||
243 | 'argumentCount' => '1+', |
||
244 | ], |
||
245 | 'AREAS' => [ |
||
246 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
247 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
248 | 'argumentCount' => '1', |
||
249 | ], |
||
250 | 'ASC' => [ |
||
251 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
252 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
253 | 'argumentCount' => '1', |
||
254 | ], |
||
255 | 'ASIN' => [ |
||
256 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
257 | 'functionCall' => 'asin', |
||
258 | 'argumentCount' => '1', |
||
259 | ], |
||
260 | 'ASINH' => [ |
||
261 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
262 | 'functionCall' => 'asinh', |
||
263 | 'argumentCount' => '1', |
||
264 | ], |
||
265 | 'ATAN' => [ |
||
266 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
267 | 'functionCall' => 'atan', |
||
268 | 'argumentCount' => '1', |
||
269 | ], |
||
270 | 'ATAN2' => [ |
||
271 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
272 | 'functionCall' => [MathTrig::class, 'ATAN2'], |
||
273 | 'argumentCount' => '2', |
||
274 | ], |
||
275 | 'ATANH' => [ |
||
276 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
277 | 'functionCall' => 'atanh', |
||
278 | 'argumentCount' => '1', |
||
279 | ], |
||
280 | 'AVEDEV' => [ |
||
281 | 'category' => Category::CATEGORY_STATISTICAL, |
||
282 | 'functionCall' => [Statistical::class, 'AVEDEV'], |
||
283 | 'argumentCount' => '1+', |
||
284 | ], |
||
285 | 'AVERAGE' => [ |
||
286 | 'category' => Category::CATEGORY_STATISTICAL, |
||
287 | 'functionCall' => [Statistical::class, 'AVERAGE'], |
||
288 | 'argumentCount' => '1+', |
||
289 | ], |
||
290 | 'AVERAGEA' => [ |
||
291 | 'category' => Category::CATEGORY_STATISTICAL, |
||
292 | 'functionCall' => [Statistical::class, 'AVERAGEA'], |
||
293 | 'argumentCount' => '1+', |
||
294 | ], |
||
295 | 'AVERAGEIF' => [ |
||
296 | 'category' => Category::CATEGORY_STATISTICAL, |
||
297 | 'functionCall' => [Statistical::class, 'AVERAGEIF'], |
||
298 | 'argumentCount' => '2,3', |
||
299 | ], |
||
300 | 'AVERAGEIFS' => [ |
||
301 | 'category' => Category::CATEGORY_STATISTICAL, |
||
302 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
303 | 'argumentCount' => '3+', |
||
304 | ], |
||
305 | 'BAHTTEXT' => [ |
||
306 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
307 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
308 | 'argumentCount' => '1', |
||
309 | ], |
||
310 | 'BESSELI' => [ |
||
311 | 'category' => Category::CATEGORY_ENGINEERING, |
||
312 | 'functionCall' => [Engineering::class, 'BESSELI'], |
||
313 | 'argumentCount' => '2', |
||
314 | ], |
||
315 | 'BESSELJ' => [ |
||
316 | 'category' => Category::CATEGORY_ENGINEERING, |
||
317 | 'functionCall' => [Engineering::class, 'BESSELJ'], |
||
318 | 'argumentCount' => '2', |
||
319 | ], |
||
320 | 'BESSELK' => [ |
||
321 | 'category' => Category::CATEGORY_ENGINEERING, |
||
322 | 'functionCall' => [Engineering::class, 'BESSELK'], |
||
323 | 'argumentCount' => '2', |
||
324 | ], |
||
325 | 'BESSELY' => [ |
||
326 | 'category' => Category::CATEGORY_ENGINEERING, |
||
327 | 'functionCall' => [Engineering::class, 'BESSELY'], |
||
328 | 'argumentCount' => '2', |
||
329 | ], |
||
330 | 'BETADIST' => [ |
||
331 | 'category' => Category::CATEGORY_STATISTICAL, |
||
332 | 'functionCall' => [Statistical::class, 'BETADIST'], |
||
333 | 'argumentCount' => '3-5', |
||
334 | ], |
||
335 | 'BETAINV' => [ |
||
336 | 'category' => Category::CATEGORY_STATISTICAL, |
||
337 | 'functionCall' => [Statistical::class, 'BETAINV'], |
||
338 | 'argumentCount' => '3-5', |
||
339 | ], |
||
340 | 'BIN2DEC' => [ |
||
341 | 'category' => Category::CATEGORY_ENGINEERING, |
||
342 | 'functionCall' => [Engineering::class, 'BINTODEC'], |
||
343 | 'argumentCount' => '1', |
||
344 | ], |
||
345 | 'BIN2HEX' => [ |
||
346 | 'category' => Category::CATEGORY_ENGINEERING, |
||
347 | 'functionCall' => [Engineering::class, 'BINTOHEX'], |
||
348 | 'argumentCount' => '1,2', |
||
349 | ], |
||
350 | 'BIN2OCT' => [ |
||
351 | 'category' => Category::CATEGORY_ENGINEERING, |
||
352 | 'functionCall' => [Engineering::class, 'BINTOOCT'], |
||
353 | 'argumentCount' => '1,2', |
||
354 | ], |
||
355 | 'BINOMDIST' => [ |
||
356 | 'category' => Category::CATEGORY_STATISTICAL, |
||
357 | 'functionCall' => [Statistical::class, 'BINOMDIST'], |
||
358 | 'argumentCount' => '4', |
||
359 | ], |
||
360 | 'CEILING' => [ |
||
361 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
362 | 'functionCall' => [MathTrig::class, 'CEILING'], |
||
363 | 'argumentCount' => '2', |
||
364 | ], |
||
365 | 'CELL' => [ |
||
366 | 'category' => Category::CATEGORY_INFORMATION, |
||
367 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
368 | 'argumentCount' => '1,2', |
||
369 | ], |
||
370 | 'CHAR' => [ |
||
371 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
372 | 'functionCall' => [TextData::class, 'CHARACTER'], |
||
373 | 'argumentCount' => '1', |
||
374 | ], |
||
375 | 'CHIDIST' => [ |
||
376 | 'category' => Category::CATEGORY_STATISTICAL, |
||
377 | 'functionCall' => [Statistical::class, 'CHIDIST'], |
||
378 | 'argumentCount' => '2', |
||
379 | ], |
||
380 | 'CHIINV' => [ |
||
381 | 'category' => Category::CATEGORY_STATISTICAL, |
||
382 | 'functionCall' => [Statistical::class, 'CHIINV'], |
||
383 | 'argumentCount' => '2', |
||
384 | ], |
||
385 | 'CHITEST' => [ |
||
386 | 'category' => Category::CATEGORY_STATISTICAL, |
||
387 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
388 | 'argumentCount' => '2', |
||
389 | ], |
||
390 | 'CHOOSE' => [ |
||
391 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
392 | 'functionCall' => [LookupRef::class, 'CHOOSE'], |
||
393 | 'argumentCount' => '2+', |
||
394 | ], |
||
395 | 'CLEAN' => [ |
||
396 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
397 | 'functionCall' => [TextData::class, 'TRIMNONPRINTABLE'], |
||
398 | 'argumentCount' => '1', |
||
399 | ], |
||
400 | 'CODE' => [ |
||
401 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
402 | 'functionCall' => [TextData::class, 'ASCIICODE'], |
||
403 | 'argumentCount' => '1', |
||
404 | ], |
||
405 | 'COLUMN' => [ |
||
406 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
407 | 'functionCall' => [LookupRef::class, 'COLUMN'], |
||
408 | 'argumentCount' => '-1', |
||
409 | 'passByReference' => [true], |
||
410 | ], |
||
411 | 'COLUMNS' => [ |
||
412 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
413 | 'functionCall' => [LookupRef::class, 'COLUMNS'], |
||
414 | 'argumentCount' => '1', |
||
415 | ], |
||
416 | 'COMBIN' => [ |
||
417 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
418 | 'functionCall' => [MathTrig::class, 'COMBIN'], |
||
419 | 'argumentCount' => '2', |
||
420 | ], |
||
421 | 'COMPLEX' => [ |
||
422 | 'category' => Category::CATEGORY_ENGINEERING, |
||
423 | 'functionCall' => [Engineering::class, 'COMPLEX'], |
||
424 | 'argumentCount' => '2,3', |
||
425 | ], |
||
426 | 'CONCATENATE' => [ |
||
427 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
428 | 'functionCall' => [TextData::class, 'CONCATENATE'], |
||
429 | 'argumentCount' => '1+', |
||
430 | ], |
||
431 | 'CONFIDENCE' => [ |
||
432 | 'category' => Category::CATEGORY_STATISTICAL, |
||
433 | 'functionCall' => [Statistical::class, 'CONFIDENCE'], |
||
434 | 'argumentCount' => '3', |
||
435 | ], |
||
436 | 'CONVERT' => [ |
||
437 | 'category' => Category::CATEGORY_ENGINEERING, |
||
438 | 'functionCall' => [Engineering::class, 'CONVERTUOM'], |
||
439 | 'argumentCount' => '3', |
||
440 | ], |
||
441 | 'CORREL' => [ |
||
442 | 'category' => Category::CATEGORY_STATISTICAL, |
||
443 | 'functionCall' => [Statistical::class, 'CORREL'], |
||
444 | 'argumentCount' => '2', |
||
445 | ], |
||
446 | 'COS' => [ |
||
447 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
448 | 'functionCall' => 'cos', |
||
449 | 'argumentCount' => '1', |
||
450 | ], |
||
451 | 'COSH' => [ |
||
452 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
453 | 'functionCall' => 'cosh', |
||
454 | 'argumentCount' => '1', |
||
455 | ], |
||
456 | 'COUNT' => [ |
||
457 | 'category' => Category::CATEGORY_STATISTICAL, |
||
458 | 'functionCall' => [Statistical::class, 'COUNT'], |
||
459 | 'argumentCount' => '1+', |
||
460 | ], |
||
461 | 'COUNTA' => [ |
||
462 | 'category' => Category::CATEGORY_STATISTICAL, |
||
463 | 'functionCall' => [Statistical::class, 'COUNTA'], |
||
464 | 'argumentCount' => '1+', |
||
465 | ], |
||
466 | 'COUNTBLANK' => [ |
||
467 | 'category' => Category::CATEGORY_STATISTICAL, |
||
468 | 'functionCall' => [Statistical::class, 'COUNTBLANK'], |
||
469 | 'argumentCount' => '1', |
||
470 | ], |
||
471 | 'COUNTIF' => [ |
||
472 | 'category' => Category::CATEGORY_STATISTICAL, |
||
473 | 'functionCall' => [Statistical::class, 'COUNTIF'], |
||
474 | 'argumentCount' => '2', |
||
475 | ], |
||
476 | 'COUNTIFS' => [ |
||
477 | 'category' => Category::CATEGORY_STATISTICAL, |
||
478 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
479 | 'argumentCount' => '2', |
||
480 | ], |
||
481 | 'COUPDAYBS' => [ |
||
482 | 'category' => Category::CATEGORY_FINANCIAL, |
||
483 | 'functionCall' => [Financial::class, 'COUPDAYBS'], |
||
484 | 'argumentCount' => '3,4', |
||
485 | ], |
||
486 | 'COUPDAYS' => [ |
||
487 | 'category' => Category::CATEGORY_FINANCIAL, |
||
488 | 'functionCall' => [Financial::class, 'COUPDAYS'], |
||
489 | 'argumentCount' => '3,4', |
||
490 | ], |
||
491 | 'COUPDAYSNC' => [ |
||
492 | 'category' => Category::CATEGORY_FINANCIAL, |
||
493 | 'functionCall' => [Financial::class, 'COUPDAYSNC'], |
||
494 | 'argumentCount' => '3,4', |
||
495 | ], |
||
496 | 'COUPNCD' => [ |
||
497 | 'category' => Category::CATEGORY_FINANCIAL, |
||
498 | 'functionCall' => [Financial::class, 'COUPNCD'], |
||
499 | 'argumentCount' => '3,4', |
||
500 | ], |
||
501 | 'COUPNUM' => [ |
||
502 | 'category' => Category::CATEGORY_FINANCIAL, |
||
503 | 'functionCall' => [Financial::class, 'COUPNUM'], |
||
504 | 'argumentCount' => '3,4', |
||
505 | ], |
||
506 | 'COUPPCD' => [ |
||
507 | 'category' => Category::CATEGORY_FINANCIAL, |
||
508 | 'functionCall' => [Financial::class, 'COUPPCD'], |
||
509 | 'argumentCount' => '3,4', |
||
510 | ], |
||
511 | 'COVAR' => [ |
||
512 | 'category' => Category::CATEGORY_STATISTICAL, |
||
513 | 'functionCall' => [Statistical::class, 'COVAR'], |
||
514 | 'argumentCount' => '2', |
||
515 | ], |
||
516 | 'CRITBINOM' => [ |
||
517 | 'category' => Category::CATEGORY_STATISTICAL, |
||
518 | 'functionCall' => [Statistical::class, 'CRITBINOM'], |
||
519 | 'argumentCount' => '3', |
||
520 | ], |
||
521 | 'CUBEKPIMEMBER' => [ |
||
522 | 'category' => Category::CATEGORY_CUBE, |
||
523 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
524 | 'argumentCount' => '?', |
||
525 | ], |
||
526 | 'CUBEMEMBER' => [ |
||
527 | 'category' => Category::CATEGORY_CUBE, |
||
528 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
529 | 'argumentCount' => '?', |
||
530 | ], |
||
531 | 'CUBEMEMBERPROPERTY' => [ |
||
532 | 'category' => Category::CATEGORY_CUBE, |
||
533 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
534 | 'argumentCount' => '?', |
||
535 | ], |
||
536 | 'CUBERANKEDMEMBER' => [ |
||
537 | 'category' => Category::CATEGORY_CUBE, |
||
538 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
539 | 'argumentCount' => '?', |
||
540 | ], |
||
541 | 'CUBESET' => [ |
||
542 | 'category' => Category::CATEGORY_CUBE, |
||
543 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
544 | 'argumentCount' => '?', |
||
545 | ], |
||
546 | 'CUBESETCOUNT' => [ |
||
547 | 'category' => Category::CATEGORY_CUBE, |
||
548 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
549 | 'argumentCount' => '?', |
||
550 | ], |
||
551 | 'CUBEVALUE' => [ |
||
552 | 'category' => Category::CATEGORY_CUBE, |
||
553 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
554 | 'argumentCount' => '?', |
||
555 | ], |
||
556 | 'CUMIPMT' => [ |
||
557 | 'category' => Category::CATEGORY_FINANCIAL, |
||
558 | 'functionCall' => [Financial::class, 'CUMIPMT'], |
||
559 | 'argumentCount' => '6', |
||
560 | ], |
||
561 | 'CUMPRINC' => [ |
||
562 | 'category' => Category::CATEGORY_FINANCIAL, |
||
563 | 'functionCall' => [Financial::class, 'CUMPRINC'], |
||
564 | 'argumentCount' => '6', |
||
565 | ], |
||
566 | 'DATE' => [ |
||
567 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
568 | 'functionCall' => [DateTime::class, 'DATE'], |
||
569 | 'argumentCount' => '3', |
||
570 | ], |
||
571 | 'DATEDIF' => [ |
||
572 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
573 | 'functionCall' => [DateTime::class, 'DATEDIF'], |
||
574 | 'argumentCount' => '2,3', |
||
575 | ], |
||
576 | 'DATEVALUE' => [ |
||
577 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
578 | 'functionCall' => [DateTime::class, 'DATEVALUE'], |
||
579 | 'argumentCount' => '1', |
||
580 | ], |
||
581 | 'DAVERAGE' => [ |
||
582 | 'category' => Category::CATEGORY_DATABASE, |
||
583 | 'functionCall' => [Database::class, 'DAVERAGE'], |
||
584 | 'argumentCount' => '3', |
||
585 | ], |
||
586 | 'DAY' => [ |
||
587 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
588 | 'functionCall' => [DateTime::class, 'DAYOFMONTH'], |
||
589 | 'argumentCount' => '1', |
||
590 | ], |
||
591 | 'DAYS360' => [ |
||
592 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
593 | 'functionCall' => [DateTime::class, 'DAYS360'], |
||
594 | 'argumentCount' => '2,3', |
||
595 | ], |
||
596 | 'DB' => [ |
||
597 | 'category' => Category::CATEGORY_FINANCIAL, |
||
598 | 'functionCall' => [Financial::class, 'DB'], |
||
599 | 'argumentCount' => '4,5', |
||
600 | ], |
||
601 | 'DCOUNT' => [ |
||
602 | 'category' => Category::CATEGORY_DATABASE, |
||
603 | 'functionCall' => [Database::class, 'DCOUNT'], |
||
604 | 'argumentCount' => '3', |
||
605 | ], |
||
606 | 'DCOUNTA' => [ |
||
607 | 'category' => Category::CATEGORY_DATABASE, |
||
608 | 'functionCall' => [Database::class, 'DCOUNTA'], |
||
609 | 'argumentCount' => '3', |
||
610 | ], |
||
611 | 'DDB' => [ |
||
612 | 'category' => Category::CATEGORY_FINANCIAL, |
||
613 | 'functionCall' => [Financial::class, 'DDB'], |
||
614 | 'argumentCount' => '4,5', |
||
615 | ], |
||
616 | 'DEC2BIN' => [ |
||
617 | 'category' => Category::CATEGORY_ENGINEERING, |
||
618 | 'functionCall' => [Engineering::class, 'DECTOBIN'], |
||
619 | 'argumentCount' => '1,2', |
||
620 | ], |
||
621 | 'DEC2HEX' => [ |
||
622 | 'category' => Category::CATEGORY_ENGINEERING, |
||
623 | 'functionCall' => [Engineering::class, 'DECTOHEX'], |
||
624 | 'argumentCount' => '1,2', |
||
625 | ], |
||
626 | 'DEC2OCT' => [ |
||
627 | 'category' => Category::CATEGORY_ENGINEERING, |
||
628 | 'functionCall' => [Engineering::class, 'DECTOOCT'], |
||
629 | 'argumentCount' => '1,2', |
||
630 | ], |
||
631 | 'DEGREES' => [ |
||
632 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
633 | 'functionCall' => 'rad2deg', |
||
634 | 'argumentCount' => '1', |
||
635 | ], |
||
636 | 'DELTA' => [ |
||
637 | 'category' => Category::CATEGORY_ENGINEERING, |
||
638 | 'functionCall' => [Engineering::class, 'DELTA'], |
||
639 | 'argumentCount' => '1,2', |
||
640 | ], |
||
641 | 'DEVSQ' => [ |
||
642 | 'category' => Category::CATEGORY_STATISTICAL, |
||
643 | 'functionCall' => [Statistical::class, 'DEVSQ'], |
||
644 | 'argumentCount' => '1+', |
||
645 | ], |
||
646 | 'DGET' => [ |
||
647 | 'category' => Category::CATEGORY_DATABASE, |
||
648 | 'functionCall' => [Database::class, 'DGET'], |
||
649 | 'argumentCount' => '3', |
||
650 | ], |
||
651 | 'DISC' => [ |
||
652 | 'category' => Category::CATEGORY_FINANCIAL, |
||
653 | 'functionCall' => [Financial::class, 'DISC'], |
||
654 | 'argumentCount' => '4,5', |
||
655 | ], |
||
656 | 'DMAX' => [ |
||
657 | 'category' => Category::CATEGORY_DATABASE, |
||
658 | 'functionCall' => [Database::class, 'DMAX'], |
||
659 | 'argumentCount' => '3', |
||
660 | ], |
||
661 | 'DMIN' => [ |
||
662 | 'category' => Category::CATEGORY_DATABASE, |
||
663 | 'functionCall' => [Database::class, 'DMIN'], |
||
664 | 'argumentCount' => '3', |
||
665 | ], |
||
666 | 'DOLLAR' => [ |
||
667 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
668 | 'functionCall' => [TextData::class, 'DOLLAR'], |
||
669 | 'argumentCount' => '1,2', |
||
670 | ], |
||
671 | 'DOLLARDE' => [ |
||
672 | 'category' => Category::CATEGORY_FINANCIAL, |
||
673 | 'functionCall' => [Financial::class, 'DOLLARDE'], |
||
674 | 'argumentCount' => '2', |
||
675 | ], |
||
676 | 'DOLLARFR' => [ |
||
677 | 'category' => Category::CATEGORY_FINANCIAL, |
||
678 | 'functionCall' => [Financial::class, 'DOLLARFR'], |
||
679 | 'argumentCount' => '2', |
||
680 | ], |
||
681 | 'DPRODUCT' => [ |
||
682 | 'category' => Category::CATEGORY_DATABASE, |
||
683 | 'functionCall' => [Database::class, 'DPRODUCT'], |
||
684 | 'argumentCount' => '3', |
||
685 | ], |
||
686 | 'DSTDEV' => [ |
||
687 | 'category' => Category::CATEGORY_DATABASE, |
||
688 | 'functionCall' => [Database::class, 'DSTDEV'], |
||
689 | 'argumentCount' => '3', |
||
690 | ], |
||
691 | 'DSTDEVP' => [ |
||
692 | 'category' => Category::CATEGORY_DATABASE, |
||
693 | 'functionCall' => [Database::class, 'DSTDEVP'], |
||
694 | 'argumentCount' => '3', |
||
695 | ], |
||
696 | 'DSUM' => [ |
||
697 | 'category' => Category::CATEGORY_DATABASE, |
||
698 | 'functionCall' => [Database::class, 'DSUM'], |
||
699 | 'argumentCount' => '3', |
||
700 | ], |
||
701 | 'DURATION' => [ |
||
702 | 'category' => Category::CATEGORY_FINANCIAL, |
||
703 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
704 | 'argumentCount' => '5,6', |
||
705 | ], |
||
706 | 'DVAR' => [ |
||
707 | 'category' => Category::CATEGORY_DATABASE, |
||
708 | 'functionCall' => [Database::class, 'DVAR'], |
||
709 | 'argumentCount' => '3', |
||
710 | ], |
||
711 | 'DVARP' => [ |
||
712 | 'category' => Category::CATEGORY_DATABASE, |
||
713 | 'functionCall' => [Database::class, 'DVARP'], |
||
714 | 'argumentCount' => '3', |
||
715 | ], |
||
716 | 'EDATE' => [ |
||
717 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
718 | 'functionCall' => [DateTime::class, 'EDATE'], |
||
719 | 'argumentCount' => '2', |
||
720 | ], |
||
721 | 'EFFECT' => [ |
||
722 | 'category' => Category::CATEGORY_FINANCIAL, |
||
723 | 'functionCall' => [Financial::class, 'EFFECT'], |
||
724 | 'argumentCount' => '2', |
||
725 | ], |
||
726 | 'EOMONTH' => [ |
||
727 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
728 | 'functionCall' => [DateTime::class, 'EOMONTH'], |
||
729 | 'argumentCount' => '2', |
||
730 | ], |
||
731 | 'ERF' => [ |
||
732 | 'category' => Category::CATEGORY_ENGINEERING, |
||
733 | 'functionCall' => [Engineering::class, 'ERF'], |
||
734 | 'argumentCount' => '1,2', |
||
735 | ], |
||
736 | 'ERFC' => [ |
||
737 | 'category' => Category::CATEGORY_ENGINEERING, |
||
738 | 'functionCall' => [Engineering::class, 'ERFC'], |
||
739 | 'argumentCount' => '1', |
||
740 | ], |
||
741 | 'ERROR.TYPE' => [ |
||
742 | 'category' => Category::CATEGORY_INFORMATION, |
||
743 | 'functionCall' => [Functions::class, 'errorType'], |
||
744 | 'argumentCount' => '1', |
||
745 | ], |
||
746 | 'EVEN' => [ |
||
747 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
748 | 'functionCall' => [MathTrig::class, 'EVEN'], |
||
749 | 'argumentCount' => '1', |
||
750 | ], |
||
751 | 'EXACT' => [ |
||
752 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
753 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
754 | 'argumentCount' => '2', |
||
755 | ], |
||
756 | 'EXP' => [ |
||
757 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
758 | 'functionCall' => 'exp', |
||
759 | 'argumentCount' => '1', |
||
760 | ], |
||
761 | 'EXPONDIST' => [ |
||
762 | 'category' => Category::CATEGORY_STATISTICAL, |
||
763 | 'functionCall' => [Statistical::class, 'EXPONDIST'], |
||
764 | 'argumentCount' => '3', |
||
765 | ], |
||
766 | 'FACT' => [ |
||
767 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
768 | 'functionCall' => [MathTrig::class, 'FACT'], |
||
769 | 'argumentCount' => '1', |
||
770 | ], |
||
771 | 'FACTDOUBLE' => [ |
||
772 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
773 | 'functionCall' => [MathTrig::class, 'FACTDOUBLE'], |
||
774 | 'argumentCount' => '1', |
||
775 | ], |
||
776 | 'FALSE' => [ |
||
777 | 'category' => Category::CATEGORY_LOGICAL, |
||
778 | 'functionCall' => [Logical::class, 'FALSE'], |
||
779 | 'argumentCount' => '0', |
||
780 | ], |
||
781 | 'FDIST' => [ |
||
782 | 'category' => Category::CATEGORY_STATISTICAL, |
||
783 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
784 | 'argumentCount' => '3', |
||
785 | ], |
||
786 | 'FIND' => [ |
||
787 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
788 | 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'], |
||
789 | 'argumentCount' => '2,3', |
||
790 | ], |
||
791 | 'FINDB' => [ |
||
792 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
793 | 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'], |
||
794 | 'argumentCount' => '2,3', |
||
795 | ], |
||
796 | 'FINV' => [ |
||
797 | 'category' => Category::CATEGORY_STATISTICAL, |
||
798 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
799 | 'argumentCount' => '3', |
||
800 | ], |
||
801 | 'FISHER' => [ |
||
802 | 'category' => Category::CATEGORY_STATISTICAL, |
||
803 | 'functionCall' => [Statistical::class, 'FISHER'], |
||
804 | 'argumentCount' => '1', |
||
805 | ], |
||
806 | 'FISHERINV' => [ |
||
807 | 'category' => Category::CATEGORY_STATISTICAL, |
||
808 | 'functionCall' => [Statistical::class, 'FISHERINV'], |
||
809 | 'argumentCount' => '1', |
||
810 | ], |
||
811 | 'FIXED' => [ |
||
812 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
813 | 'functionCall' => [TextData::class, 'FIXEDFORMAT'], |
||
814 | 'argumentCount' => '1-3', |
||
815 | ], |
||
816 | 'FLOOR' => [ |
||
817 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
818 | 'functionCall' => [MathTrig::class, 'FLOOR'], |
||
819 | 'argumentCount' => '2', |
||
820 | ], |
||
821 | 'FORECAST' => [ |
||
822 | 'category' => Category::CATEGORY_STATISTICAL, |
||
823 | 'functionCall' => [Statistical::class, 'FORECAST'], |
||
824 | 'argumentCount' => '3', |
||
825 | ], |
||
826 | 'FREQUENCY' => [ |
||
827 | 'category' => Category::CATEGORY_STATISTICAL, |
||
828 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
829 | 'argumentCount' => '2', |
||
830 | ], |
||
831 | 'FTEST' => [ |
||
832 | 'category' => Category::CATEGORY_STATISTICAL, |
||
833 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
834 | 'argumentCount' => '2', |
||
835 | ], |
||
836 | 'FV' => [ |
||
837 | 'category' => Category::CATEGORY_FINANCIAL, |
||
838 | 'functionCall' => [Financial::class, 'FV'], |
||
839 | 'argumentCount' => '3-5', |
||
840 | ], |
||
841 | 'FVSCHEDULE' => [ |
||
842 | 'category' => Category::CATEGORY_FINANCIAL, |
||
843 | 'functionCall' => [Financial::class, 'FVSCHEDULE'], |
||
844 | 'argumentCount' => '2', |
||
845 | ], |
||
846 | 'GAMMADIST' => [ |
||
847 | 'category' => Category::CATEGORY_STATISTICAL, |
||
848 | 'functionCall' => [Statistical::class, 'GAMMADIST'], |
||
849 | 'argumentCount' => '4', |
||
850 | ], |
||
851 | 'GAMMAINV' => [ |
||
852 | 'category' => Category::CATEGORY_STATISTICAL, |
||
853 | 'functionCall' => [Statistical::class, 'GAMMAINV'], |
||
854 | 'argumentCount' => '3', |
||
855 | ], |
||
856 | 'GAMMALN' => [ |
||
857 | 'category' => Category::CATEGORY_STATISTICAL, |
||
858 | 'functionCall' => [Statistical::class, 'GAMMALN'], |
||
859 | 'argumentCount' => '1', |
||
860 | ], |
||
861 | 'GCD' => [ |
||
862 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
863 | 'functionCall' => [MathTrig::class, 'GCD'], |
||
864 | 'argumentCount' => '1+', |
||
865 | ], |
||
866 | 'GEOMEAN' => [ |
||
867 | 'category' => Category::CATEGORY_STATISTICAL, |
||
868 | 'functionCall' => [Statistical::class, 'GEOMEAN'], |
||
869 | 'argumentCount' => '1+', |
||
870 | ], |
||
871 | 'GESTEP' => [ |
||
872 | 'category' => Category::CATEGORY_ENGINEERING, |
||
873 | 'functionCall' => [Engineering::class, 'GESTEP'], |
||
874 | 'argumentCount' => '1,2', |
||
875 | ], |
||
876 | 'GETPIVOTDATA' => [ |
||
877 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
878 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
879 | 'argumentCount' => '2+', |
||
880 | ], |
||
881 | 'GROWTH' => [ |
||
882 | 'category' => Category::CATEGORY_STATISTICAL, |
||
883 | 'functionCall' => [Statistical::class, 'GROWTH'], |
||
884 | 'argumentCount' => '1-4', |
||
885 | ], |
||
886 | 'HARMEAN' => [ |
||
887 | 'category' => Category::CATEGORY_STATISTICAL, |
||
888 | 'functionCall' => [Statistical::class, 'HARMEAN'], |
||
889 | 'argumentCount' => '1+', |
||
890 | ], |
||
891 | 'HEX2BIN' => [ |
||
892 | 'category' => Category::CATEGORY_ENGINEERING, |
||
893 | 'functionCall' => [Engineering::class, 'HEXTOBIN'], |
||
894 | 'argumentCount' => '1,2', |
||
895 | ], |
||
896 | 'HEX2DEC' => [ |
||
897 | 'category' => Category::CATEGORY_ENGINEERING, |
||
898 | 'functionCall' => [Engineering::class, 'HEXTODEC'], |
||
899 | 'argumentCount' => '1', |
||
900 | ], |
||
901 | 'HEX2OCT' => [ |
||
902 | 'category' => Category::CATEGORY_ENGINEERING, |
||
903 | 'functionCall' => [Engineering::class, 'HEXTOOCT'], |
||
904 | 'argumentCount' => '1,2', |
||
905 | ], |
||
906 | 'HLOOKUP' => [ |
||
907 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
908 | 'functionCall' => [LookupRef::class, 'HLOOKUP'], |
||
909 | 'argumentCount' => '3,4', |
||
910 | ], |
||
911 | 'HOUR' => [ |
||
912 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
913 | 'functionCall' => [DateTime::class, 'HOUROFDAY'], |
||
914 | 'argumentCount' => '1', |
||
915 | ], |
||
916 | 'HYPERLINK' => [ |
||
917 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
918 | 'functionCall' => [LookupRef::class, 'HYPERLINK'], |
||
919 | 'argumentCount' => '1,2', |
||
920 | 'passCellReference' => true, |
||
921 | ], |
||
922 | 'HYPGEOMDIST' => [ |
||
923 | 'category' => Category::CATEGORY_STATISTICAL, |
||
924 | 'functionCall' => [Statistical::class, 'HYPGEOMDIST'], |
||
925 | 'argumentCount' => '4', |
||
926 | ], |
||
927 | 'IF' => [ |
||
928 | 'category' => Category::CATEGORY_LOGICAL, |
||
929 | 'functionCall' => [Logical::class, 'statementIf'], |
||
930 | 'argumentCount' => '1-3', |
||
931 | ], |
||
932 | 'IFERROR' => [ |
||
933 | 'category' => Category::CATEGORY_LOGICAL, |
||
934 | 'functionCall' => [Logical::class, 'IFERROR'], |
||
935 | 'argumentCount' => '2', |
||
936 | ], |
||
937 | 'IMABS' => [ |
||
938 | 'category' => Category::CATEGORY_ENGINEERING, |
||
939 | 'functionCall' => [Engineering::class, 'IMABS'], |
||
940 | 'argumentCount' => '1', |
||
941 | ], |
||
942 | 'IMAGINARY' => [ |
||
943 | 'category' => Category::CATEGORY_ENGINEERING, |
||
944 | 'functionCall' => [Engineering::class, 'IMAGINARY'], |
||
945 | 'argumentCount' => '1', |
||
946 | ], |
||
947 | 'IMARGUMENT' => [ |
||
948 | 'category' => Category::CATEGORY_ENGINEERING, |
||
949 | 'functionCall' => [Engineering::class, 'IMARGUMENT'], |
||
950 | 'argumentCount' => '1', |
||
951 | ], |
||
952 | 'IMCONJUGATE' => [ |
||
953 | 'category' => Category::CATEGORY_ENGINEERING, |
||
954 | 'functionCall' => [Engineering::class, 'IMCONJUGATE'], |
||
955 | 'argumentCount' => '1', |
||
956 | ], |
||
957 | 'IMCOS' => [ |
||
958 | 'category' => Category::CATEGORY_ENGINEERING, |
||
959 | 'functionCall' => [Engineering::class, 'IMCOS'], |
||
960 | 'argumentCount' => '1', |
||
961 | ], |
||
962 | 'IMDIV' => [ |
||
963 | 'category' => Category::CATEGORY_ENGINEERING, |
||
964 | 'functionCall' => [Engineering::class, 'IMDIV'], |
||
965 | 'argumentCount' => '2', |
||
966 | ], |
||
967 | 'IMEXP' => [ |
||
968 | 'category' => Category::CATEGORY_ENGINEERING, |
||
969 | 'functionCall' => [Engineering::class, 'IMEXP'], |
||
970 | 'argumentCount' => '1', |
||
971 | ], |
||
972 | 'IMLN' => [ |
||
973 | 'category' => Category::CATEGORY_ENGINEERING, |
||
974 | 'functionCall' => [Engineering::class, 'IMLN'], |
||
975 | 'argumentCount' => '1', |
||
976 | ], |
||
977 | 'IMLOG10' => [ |
||
978 | 'category' => Category::CATEGORY_ENGINEERING, |
||
979 | 'functionCall' => [Engineering::class, 'IMLOG10'], |
||
980 | 'argumentCount' => '1', |
||
981 | ], |
||
982 | 'IMLOG2' => [ |
||
983 | 'category' => Category::CATEGORY_ENGINEERING, |
||
984 | 'functionCall' => [Engineering::class, 'IMLOG2'], |
||
985 | 'argumentCount' => '1', |
||
986 | ], |
||
987 | 'IMPOWER' => [ |
||
988 | 'category' => Category::CATEGORY_ENGINEERING, |
||
989 | 'functionCall' => [Engineering::class, 'IMPOWER'], |
||
990 | 'argumentCount' => '2', |
||
991 | ], |
||
992 | 'IMPRODUCT' => [ |
||
993 | 'category' => Category::CATEGORY_ENGINEERING, |
||
994 | 'functionCall' => [Engineering::class, 'IMPRODUCT'], |
||
995 | 'argumentCount' => '1+', |
||
996 | ], |
||
997 | 'IMREAL' => [ |
||
998 | 'category' => Category::CATEGORY_ENGINEERING, |
||
999 | 'functionCall' => [Engineering::class, 'IMREAL'], |
||
1000 | 'argumentCount' => '1', |
||
1001 | ], |
||
1002 | 'IMSIN' => [ |
||
1003 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1004 | 'functionCall' => [Engineering::class, 'IMSIN'], |
||
1005 | 'argumentCount' => '1', |
||
1006 | ], |
||
1007 | 'IMSQRT' => [ |
||
1008 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1009 | 'functionCall' => [Engineering::class, 'IMSQRT'], |
||
1010 | 'argumentCount' => '1', |
||
1011 | ], |
||
1012 | 'IMSUB' => [ |
||
1013 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1014 | 'functionCall' => [Engineering::class, 'IMSUB'], |
||
1015 | 'argumentCount' => '2', |
||
1016 | ], |
||
1017 | 'IMSUM' => [ |
||
1018 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1019 | 'functionCall' => [Engineering::class, 'IMSUM'], |
||
1020 | 'argumentCount' => '1+', |
||
1021 | ], |
||
1022 | 'INDEX' => [ |
||
1023 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1024 | 'functionCall' => [LookupRef::class, 'INDEX'], |
||
1025 | 'argumentCount' => '1-4', |
||
1026 | ], |
||
1027 | 'INDIRECT' => [ |
||
1028 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1029 | 'functionCall' => [LookupRef::class, 'INDIRECT'], |
||
1030 | 'argumentCount' => '1,2', |
||
1031 | 'passCellReference' => true, |
||
1032 | ], |
||
1033 | 'INFO' => [ |
||
1034 | 'category' => Category::CATEGORY_INFORMATION, |
||
1035 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1036 | 'argumentCount' => '1', |
||
1037 | ], |
||
1038 | 'INT' => [ |
||
1039 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1040 | 'functionCall' => [MathTrig::class, 'INT'], |
||
1041 | 'argumentCount' => '1', |
||
1042 | ], |
||
1043 | 'INTERCEPT' => [ |
||
1044 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1045 | 'functionCall' => [Statistical::class, 'INTERCEPT'], |
||
1046 | 'argumentCount' => '2', |
||
1047 | ], |
||
1048 | 'INTRATE' => [ |
||
1049 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1050 | 'functionCall' => [Financial::class, 'INTRATE'], |
||
1051 | 'argumentCount' => '4,5', |
||
1052 | ], |
||
1053 | 'IPMT' => [ |
||
1054 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1055 | 'functionCall' => [Financial::class, 'IPMT'], |
||
1056 | 'argumentCount' => '4-6', |
||
1057 | ], |
||
1058 | 'IRR' => [ |
||
1059 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1060 | 'functionCall' => [Financial::class, 'IRR'], |
||
1061 | 'argumentCount' => '1,2', |
||
1062 | ], |
||
1063 | 'ISBLANK' => [ |
||
1064 | 'category' => Category::CATEGORY_INFORMATION, |
||
1065 | 'functionCall' => [Functions::class, 'isBlank'], |
||
1066 | 'argumentCount' => '1', |
||
1067 | ], |
||
1068 | 'ISERR' => [ |
||
1069 | 'category' => Category::CATEGORY_INFORMATION, |
||
1070 | 'functionCall' => [Functions::class, 'isErr'], |
||
1071 | 'argumentCount' => '1', |
||
1072 | ], |
||
1073 | 'ISERROR' => [ |
||
1074 | 'category' => Category::CATEGORY_INFORMATION, |
||
1075 | 'functionCall' => [Functions::class, 'isError'], |
||
1076 | 'argumentCount' => '1', |
||
1077 | ], |
||
1078 | 'ISEVEN' => [ |
||
1079 | 'category' => Category::CATEGORY_INFORMATION, |
||
1080 | 'functionCall' => [Functions::class, 'isEven'], |
||
1081 | 'argumentCount' => '1', |
||
1082 | ], |
||
1083 | 'ISLOGICAL' => [ |
||
1084 | 'category' => Category::CATEGORY_INFORMATION, |
||
1085 | 'functionCall' => [Functions::class, 'isLogical'], |
||
1086 | 'argumentCount' => '1', |
||
1087 | ], |
||
1088 | 'ISNA' => [ |
||
1089 | 'category' => Category::CATEGORY_INFORMATION, |
||
1090 | 'functionCall' => [Functions::class, 'isNa'], |
||
1091 | 'argumentCount' => '1', |
||
1092 | ], |
||
1093 | 'ISNONTEXT' => [ |
||
1094 | 'category' => Category::CATEGORY_INFORMATION, |
||
1095 | 'functionCall' => [Functions::class, 'isNonText'], |
||
1096 | 'argumentCount' => '1', |
||
1097 | ], |
||
1098 | 'ISNUMBER' => [ |
||
1099 | 'category' => Category::CATEGORY_INFORMATION, |
||
1100 | 'functionCall' => [Functions::class, 'isNumber'], |
||
1101 | 'argumentCount' => '1', |
||
1102 | ], |
||
1103 | 'ISODD' => [ |
||
1104 | 'category' => Category::CATEGORY_INFORMATION, |
||
1105 | 'functionCall' => [Functions::class, 'isOdd'], |
||
1106 | 'argumentCount' => '1', |
||
1107 | ], |
||
1108 | 'ISPMT' => [ |
||
1109 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1110 | 'functionCall' => [Financial::class, 'ISPMT'], |
||
1111 | 'argumentCount' => '4', |
||
1112 | ], |
||
1113 | 'ISREF' => [ |
||
1114 | 'category' => Category::CATEGORY_INFORMATION, |
||
1115 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1116 | 'argumentCount' => '1', |
||
1117 | ], |
||
1118 | 'ISTEXT' => [ |
||
1119 | 'category' => Category::CATEGORY_INFORMATION, |
||
1120 | 'functionCall' => [Functions::class, 'isText'], |
||
1121 | 'argumentCount' => '1', |
||
1122 | ], |
||
1123 | 'JIS' => [ |
||
1124 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1125 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1126 | 'argumentCount' => '1', |
||
1127 | ], |
||
1128 | 'KURT' => [ |
||
1129 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1130 | 'functionCall' => [Statistical::class, 'KURT'], |
||
1131 | 'argumentCount' => '1+', |
||
1132 | ], |
||
1133 | 'LARGE' => [ |
||
1134 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1135 | 'functionCall' => [Statistical::class, 'LARGE'], |
||
1136 | 'argumentCount' => '2', |
||
1137 | ], |
||
1138 | 'LCM' => [ |
||
1139 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1140 | 'functionCall' => [MathTrig::class, 'LCM'], |
||
1141 | 'argumentCount' => '1+', |
||
1142 | ], |
||
1143 | 'LEFT' => [ |
||
1144 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1145 | 'functionCall' => [TextData::class, 'LEFT'], |
||
1146 | 'argumentCount' => '1,2', |
||
1147 | ], |
||
1148 | 'LEFTB' => [ |
||
1149 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1150 | 'functionCall' => [TextData::class, 'LEFT'], |
||
1151 | 'argumentCount' => '1,2', |
||
1152 | ], |
||
1153 | 'LEN' => [ |
||
1154 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1155 | 'functionCall' => [TextData::class, 'STRINGLENGTH'], |
||
1156 | 'argumentCount' => '1', |
||
1157 | ], |
||
1158 | 'LENB' => [ |
||
1159 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1160 | 'functionCall' => [TextData::class, 'STRINGLENGTH'], |
||
1161 | 'argumentCount' => '1', |
||
1162 | ], |
||
1163 | 'LINEST' => [ |
||
1164 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1165 | 'functionCall' => [Statistical::class, 'LINEST'], |
||
1166 | 'argumentCount' => '1-4', |
||
1167 | ], |
||
1168 | 'LN' => [ |
||
1169 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1170 | 'functionCall' => 'log', |
||
1171 | 'argumentCount' => '1', |
||
1172 | ], |
||
1173 | 'LOG' => [ |
||
1174 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1175 | 'functionCall' => [MathTrig::class, 'logBase'], |
||
1176 | 'argumentCount' => '1,2', |
||
1177 | ], |
||
1178 | 'LOG10' => [ |
||
1179 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1180 | 'functionCall' => 'log10', |
||
1181 | 'argumentCount' => '1', |
||
1182 | ], |
||
1183 | 'LOGEST' => [ |
||
1184 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1185 | 'functionCall' => [Statistical::class, 'LOGEST'], |
||
1186 | 'argumentCount' => '1-4', |
||
1187 | ], |
||
1188 | 'LOGINV' => [ |
||
1189 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1190 | 'functionCall' => [Statistical::class, 'LOGINV'], |
||
1191 | 'argumentCount' => '3', |
||
1192 | ], |
||
1193 | 'LOGNORMDIST' => [ |
||
1194 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1195 | 'functionCall' => [Statistical::class, 'LOGNORMDIST'], |
||
1196 | 'argumentCount' => '3', |
||
1197 | ], |
||
1198 | 'LOOKUP' => [ |
||
1199 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1200 | 'functionCall' => [LookupRef::class, 'LOOKUP'], |
||
1201 | 'argumentCount' => '2,3', |
||
1202 | ], |
||
1203 | 'LOWER' => [ |
||
1204 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1205 | 'functionCall' => [TextData::class, 'LOWERCASE'], |
||
1206 | 'argumentCount' => '1', |
||
1207 | ], |
||
1208 | 'MATCH' => [ |
||
1209 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1210 | 'functionCall' => [LookupRef::class, 'MATCH'], |
||
1211 | 'argumentCount' => '2,3', |
||
1212 | ], |
||
1213 | 'MAX' => [ |
||
1214 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1215 | 'functionCall' => [Statistical::class, 'MAX'], |
||
1216 | 'argumentCount' => '1+', |
||
1217 | ], |
||
1218 | 'MAXA' => [ |
||
1219 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1220 | 'functionCall' => [Statistical::class, 'MAXA'], |
||
1221 | 'argumentCount' => '1+', |
||
1222 | ], |
||
1223 | 'MAXIF' => [ |
||
1224 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1225 | 'functionCall' => [Statistical::class, 'MAXIF'], |
||
1226 | 'argumentCount' => '2+', |
||
1227 | ], |
||
1228 | 'MDETERM' => [ |
||
1229 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1230 | 'functionCall' => [MathTrig::class, 'MDETERM'], |
||
1231 | 'argumentCount' => '1', |
||
1232 | ], |
||
1233 | 'MDURATION' => [ |
||
1234 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1235 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1236 | 'argumentCount' => '5,6', |
||
1237 | ], |
||
1238 | 'MEDIAN' => [ |
||
1239 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1240 | 'functionCall' => [Statistical::class, 'MEDIAN'], |
||
1241 | 'argumentCount' => '1+', |
||
1242 | ], |
||
1243 | 'MEDIANIF' => [ |
||
1244 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1245 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1246 | 'argumentCount' => '2+', |
||
1247 | ], |
||
1248 | 'MID' => [ |
||
1249 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1250 | 'functionCall' => [TextData::class, 'MID'], |
||
1251 | 'argumentCount' => '3', |
||
1252 | ], |
||
1253 | 'MIDB' => [ |
||
1254 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1255 | 'functionCall' => [TextData::class, 'MID'], |
||
1256 | 'argumentCount' => '3', |
||
1257 | ], |
||
1258 | 'MIN' => [ |
||
1259 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1260 | 'functionCall' => [Statistical::class, 'MIN'], |
||
1261 | 'argumentCount' => '1+', |
||
1262 | ], |
||
1263 | 'MINA' => [ |
||
1264 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1265 | 'functionCall' => [Statistical::class, 'MINA'], |
||
1266 | 'argumentCount' => '1+', |
||
1267 | ], |
||
1268 | 'MINIF' => [ |
||
1269 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1270 | 'functionCall' => [Statistical::class, 'MINIF'], |
||
1271 | 'argumentCount' => '2+', |
||
1272 | ], |
||
1273 | 'MINUTE' => [ |
||
1274 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1275 | 'functionCall' => [DateTime::class, 'MINUTE'], |
||
1276 | 'argumentCount' => '1', |
||
1277 | ], |
||
1278 | 'MINVERSE' => [ |
||
1279 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1280 | 'functionCall' => [MathTrig::class, 'MINVERSE'], |
||
1281 | 'argumentCount' => '1', |
||
1282 | ], |
||
1283 | 'MIRR' => [ |
||
1284 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1285 | 'functionCall' => [Financial::class, 'MIRR'], |
||
1286 | 'argumentCount' => '3', |
||
1287 | ], |
||
1288 | 'MMULT' => [ |
||
1289 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1290 | 'functionCall' => [MathTrig::class, 'MMULT'], |
||
1291 | 'argumentCount' => '2', |
||
1292 | ], |
||
1293 | 'MOD' => [ |
||
1294 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1295 | 'functionCall' => [MathTrig::class, 'MOD'], |
||
1296 | 'argumentCount' => '2', |
||
1297 | ], |
||
1298 | 'MODE' => [ |
||
1299 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1300 | 'functionCall' => [Statistical::class, 'MODE'], |
||
1301 | 'argumentCount' => '1+', |
||
1302 | ], |
||
1303 | 'MONTH' => [ |
||
1304 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1305 | 'functionCall' => [DateTime::class, 'MONTHOFYEAR'], |
||
1306 | 'argumentCount' => '1', |
||
1307 | ], |
||
1308 | 'MROUND' => [ |
||
1309 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1310 | 'functionCall' => [MathTrig::class, 'MROUND'], |
||
1311 | 'argumentCount' => '2', |
||
1312 | ], |
||
1313 | 'MULTINOMIAL' => [ |
||
1314 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1315 | 'functionCall' => [MathTrig::class, 'MULTINOMIAL'], |
||
1316 | 'argumentCount' => '1+', |
||
1317 | ], |
||
1318 | 'N' => [ |
||
1319 | 'category' => Category::CATEGORY_INFORMATION, |
||
1320 | 'functionCall' => [Functions::class, 'n'], |
||
1321 | 'argumentCount' => '1', |
||
1322 | ], |
||
1323 | 'NA' => [ |
||
1324 | 'category' => Category::CATEGORY_INFORMATION, |
||
1325 | 'functionCall' => [Functions::class, 'NA'], |
||
1326 | 'argumentCount' => '0', |
||
1327 | ], |
||
1328 | 'NEGBINOMDIST' => [ |
||
1329 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1330 | 'functionCall' => [Statistical::class, 'NEGBINOMDIST'], |
||
1331 | 'argumentCount' => '3', |
||
1332 | ], |
||
1333 | 'NETWORKDAYS' => [ |
||
1334 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1335 | 'functionCall' => [DateTime::class, 'NETWORKDAYS'], |
||
1336 | 'argumentCount' => '2+', |
||
1337 | ], |
||
1338 | 'NOMINAL' => [ |
||
1339 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1340 | 'functionCall' => [Financial::class, 'NOMINAL'], |
||
1341 | 'argumentCount' => '2', |
||
1342 | ], |
||
1343 | 'NORMDIST' => [ |
||
1344 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1345 | 'functionCall' => [Statistical::class, 'NORMDIST'], |
||
1346 | 'argumentCount' => '4', |
||
1347 | ], |
||
1348 | 'NORMINV' => [ |
||
1349 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1350 | 'functionCall' => [Statistical::class, 'NORMINV'], |
||
1351 | 'argumentCount' => '3', |
||
1352 | ], |
||
1353 | 'NORMSDIST' => [ |
||
1354 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1355 | 'functionCall' => [Statistical::class, 'NORMSDIST'], |
||
1356 | 'argumentCount' => '1', |
||
1357 | ], |
||
1358 | 'NORMSINV' => [ |
||
1359 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1360 | 'functionCall' => [Statistical::class, 'NORMSINV'], |
||
1361 | 'argumentCount' => '1', |
||
1362 | ], |
||
1363 | 'NOT' => [ |
||
1364 | 'category' => Category::CATEGORY_LOGICAL, |
||
1365 | 'functionCall' => [Logical::class, 'NOT'], |
||
1366 | 'argumentCount' => '1', |
||
1367 | ], |
||
1368 | 'NOW' => [ |
||
1369 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1370 | 'functionCall' => [DateTime::class, 'DATETIMENOW'], |
||
1371 | 'argumentCount' => '0', |
||
1372 | ], |
||
1373 | 'NPER' => [ |
||
1374 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1375 | 'functionCall' => [Financial::class, 'NPER'], |
||
1376 | 'argumentCount' => '3-5', |
||
1377 | ], |
||
1378 | 'NPV' => [ |
||
1379 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1380 | 'functionCall' => [Financial::class, 'NPV'], |
||
1381 | 'argumentCount' => '2+', |
||
1382 | ], |
||
1383 | 'OCT2BIN' => [ |
||
1384 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1385 | 'functionCall' => [Engineering::class, 'OCTTOBIN'], |
||
1386 | 'argumentCount' => '1,2', |
||
1387 | ], |
||
1388 | 'OCT2DEC' => [ |
||
1389 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1390 | 'functionCall' => [Engineering::class, 'OCTTODEC'], |
||
1391 | 'argumentCount' => '1', |
||
1392 | ], |
||
1393 | 'OCT2HEX' => [ |
||
1394 | 'category' => Category::CATEGORY_ENGINEERING, |
||
1395 | 'functionCall' => [Engineering::class, 'OCTTOHEX'], |
||
1396 | 'argumentCount' => '1,2', |
||
1397 | ], |
||
1398 | 'ODD' => [ |
||
1399 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1400 | 'functionCall' => [MathTrig::class, 'ODD'], |
||
1401 | 'argumentCount' => '1', |
||
1402 | ], |
||
1403 | 'ODDFPRICE' => [ |
||
1404 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1405 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1406 | 'argumentCount' => '8,9', |
||
1407 | ], |
||
1408 | 'ODDFYIELD' => [ |
||
1409 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1410 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1411 | 'argumentCount' => '8,9', |
||
1412 | ], |
||
1413 | 'ODDLPRICE' => [ |
||
1414 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1415 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1416 | 'argumentCount' => '7,8', |
||
1417 | ], |
||
1418 | 'ODDLYIELD' => [ |
||
1419 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1420 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1421 | 'argumentCount' => '7,8', |
||
1422 | ], |
||
1423 | 'OFFSET' => [ |
||
1424 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1425 | 'functionCall' => [LookupRef::class, 'OFFSET'], |
||
1426 | 'argumentCount' => '3-5', |
||
1427 | 'passCellReference' => true, |
||
1428 | 'passByReference' => [true], |
||
1429 | ], |
||
1430 | 'OR' => [ |
||
1431 | 'category' => Category::CATEGORY_LOGICAL, |
||
1432 | 'functionCall' => [Logical::class, 'logicalOr'], |
||
1433 | 'argumentCount' => '1+', |
||
1434 | ], |
||
1435 | 'PEARSON' => [ |
||
1436 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1437 | 'functionCall' => [Statistical::class, 'CORREL'], |
||
1438 | 'argumentCount' => '2', |
||
1439 | ], |
||
1440 | 'PERCENTILE' => [ |
||
1441 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1442 | 'functionCall' => [Statistical::class, 'PERCENTILE'], |
||
1443 | 'argumentCount' => '2', |
||
1444 | ], |
||
1445 | 'PERCENTRANK' => [ |
||
1446 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1447 | 'functionCall' => [Statistical::class, 'PERCENTRANK'], |
||
1448 | 'argumentCount' => '2,3', |
||
1449 | ], |
||
1450 | 'PERMUT' => [ |
||
1451 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1452 | 'functionCall' => [Statistical::class, 'PERMUT'], |
||
1453 | 'argumentCount' => '2', |
||
1454 | ], |
||
1455 | 'PHONETIC' => [ |
||
1456 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1457 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1458 | 'argumentCount' => '1', |
||
1459 | ], |
||
1460 | 'PI' => [ |
||
1461 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1462 | 'functionCall' => 'pi', |
||
1463 | 'argumentCount' => '0', |
||
1464 | ], |
||
1465 | 'PMT' => [ |
||
1466 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1467 | 'functionCall' => [Financial::class, 'PMT'], |
||
1468 | 'argumentCount' => '3-5', |
||
1469 | ], |
||
1470 | 'POISSON' => [ |
||
1471 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1472 | 'functionCall' => [Statistical::class, 'POISSON'], |
||
1473 | 'argumentCount' => '3', |
||
1474 | ], |
||
1475 | 'POWER' => [ |
||
1476 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1477 | 'functionCall' => [MathTrig::class, 'POWER'], |
||
1478 | 'argumentCount' => '2', |
||
1479 | ], |
||
1480 | 'PPMT' => [ |
||
1481 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1482 | 'functionCall' => [Financial::class, 'PPMT'], |
||
1483 | 'argumentCount' => '4-6', |
||
1484 | ], |
||
1485 | 'PRICE' => [ |
||
1486 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1487 | 'functionCall' => [Financial::class, 'PRICE'], |
||
1488 | 'argumentCount' => '6,7', |
||
1489 | ], |
||
1490 | 'PRICEDISC' => [ |
||
1491 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1492 | 'functionCall' => [Financial::class, 'PRICEDISC'], |
||
1493 | 'argumentCount' => '4,5', |
||
1494 | ], |
||
1495 | 'PRICEMAT' => [ |
||
1496 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1497 | 'functionCall' => [Financial::class, 'PRICEMAT'], |
||
1498 | 'argumentCount' => '5,6', |
||
1499 | ], |
||
1500 | 'PROB' => [ |
||
1501 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1502 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1503 | 'argumentCount' => '3,4', |
||
1504 | ], |
||
1505 | 'PRODUCT' => [ |
||
1506 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1507 | 'functionCall' => [MathTrig::class, 'PRODUCT'], |
||
1508 | 'argumentCount' => '1+', |
||
1509 | ], |
||
1510 | 'PROPER' => [ |
||
1511 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1512 | 'functionCall' => [TextData::class, 'PROPERCASE'], |
||
1513 | 'argumentCount' => '1', |
||
1514 | ], |
||
1515 | 'PV' => [ |
||
1516 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1517 | 'functionCall' => [Financial::class, 'PV'], |
||
1518 | 'argumentCount' => '3-5', |
||
1519 | ], |
||
1520 | 'QUARTILE' => [ |
||
1521 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1522 | 'functionCall' => [Statistical::class, 'QUARTILE'], |
||
1523 | 'argumentCount' => '2', |
||
1524 | ], |
||
1525 | 'QUOTIENT' => [ |
||
1526 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1527 | 'functionCall' => [MathTrig::class, 'QUOTIENT'], |
||
1528 | 'argumentCount' => '2', |
||
1529 | ], |
||
1530 | 'RADIANS' => [ |
||
1531 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1532 | 'functionCall' => 'deg2rad', |
||
1533 | 'argumentCount' => '1', |
||
1534 | ], |
||
1535 | 'RAND' => [ |
||
1536 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1537 | 'functionCall' => [MathTrig::class, 'RAND'], |
||
1538 | 'argumentCount' => '0', |
||
1539 | ], |
||
1540 | 'RANDBETWEEN' => [ |
||
1541 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1542 | 'functionCall' => [MathTrig::class, 'RAND'], |
||
1543 | 'argumentCount' => '2', |
||
1544 | ], |
||
1545 | 'RANK' => [ |
||
1546 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1547 | 'functionCall' => [Statistical::class, 'RANK'], |
||
1548 | 'argumentCount' => '2,3', |
||
1549 | ], |
||
1550 | 'RATE' => [ |
||
1551 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1552 | 'functionCall' => [Financial::class, 'RATE'], |
||
1553 | 'argumentCount' => '3-6', |
||
1554 | ], |
||
1555 | 'RECEIVED' => [ |
||
1556 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1557 | 'functionCall' => [Financial::class, 'RECEIVED'], |
||
1558 | 'argumentCount' => '4-5', |
||
1559 | ], |
||
1560 | 'REPLACE' => [ |
||
1561 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1562 | 'functionCall' => [TextData::class, 'REPLACE'], |
||
1563 | 'argumentCount' => '4', |
||
1564 | ], |
||
1565 | 'REPLACEB' => [ |
||
1566 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1567 | 'functionCall' => [TextData::class, 'REPLACE'], |
||
1568 | 'argumentCount' => '4', |
||
1569 | ], |
||
1570 | 'REPT' => [ |
||
1571 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1572 | 'functionCall' => 'str_repeat', |
||
1573 | 'argumentCount' => '2', |
||
1574 | ], |
||
1575 | 'RIGHT' => [ |
||
1576 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1577 | 'functionCall' => [TextData::class, 'RIGHT'], |
||
1578 | 'argumentCount' => '1,2', |
||
1579 | ], |
||
1580 | 'RIGHTB' => [ |
||
1581 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1582 | 'functionCall' => [TextData::class, 'RIGHT'], |
||
1583 | 'argumentCount' => '1,2', |
||
1584 | ], |
||
1585 | 'ROMAN' => [ |
||
1586 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1587 | 'functionCall' => [MathTrig::class, 'ROMAN'], |
||
1588 | 'argumentCount' => '1,2', |
||
1589 | ], |
||
1590 | 'ROUND' => [ |
||
1591 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1592 | 'functionCall' => 'round', |
||
1593 | 'argumentCount' => '2', |
||
1594 | ], |
||
1595 | 'ROUNDDOWN' => [ |
||
1596 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1597 | 'functionCall' => [MathTrig::class, 'ROUNDDOWN'], |
||
1598 | 'argumentCount' => '2', |
||
1599 | ], |
||
1600 | 'ROUNDUP' => [ |
||
1601 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1602 | 'functionCall' => [MathTrig::class, 'ROUNDUP'], |
||
1603 | 'argumentCount' => '2', |
||
1604 | ], |
||
1605 | 'ROW' => [ |
||
1606 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1607 | 'functionCall' => [LookupRef::class, 'ROW'], |
||
1608 | 'argumentCount' => '-1', |
||
1609 | 'passByReference' => [true], |
||
1610 | ], |
||
1611 | 'ROWS' => [ |
||
1612 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1613 | 'functionCall' => [LookupRef::class, 'ROWS'], |
||
1614 | 'argumentCount' => '1', |
||
1615 | ], |
||
1616 | 'RSQ' => [ |
||
1617 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1618 | 'functionCall' => [Statistical::class, 'RSQ'], |
||
1619 | 'argumentCount' => '2', |
||
1620 | ], |
||
1621 | 'RTD' => [ |
||
1622 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1623 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1624 | 'argumentCount' => '1+', |
||
1625 | ], |
||
1626 | 'SEARCH' => [ |
||
1627 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1628 | 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'], |
||
1629 | 'argumentCount' => '2,3', |
||
1630 | ], |
||
1631 | 'SEARCHB' => [ |
||
1632 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1633 | 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'], |
||
1634 | 'argumentCount' => '2,3', |
||
1635 | ], |
||
1636 | 'SECOND' => [ |
||
1637 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1638 | 'functionCall' => [DateTime::class, 'SECOND'], |
||
1639 | 'argumentCount' => '1', |
||
1640 | ], |
||
1641 | 'SERIESSUM' => [ |
||
1642 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1643 | 'functionCall' => [MathTrig::class, 'SERIESSUM'], |
||
1644 | 'argumentCount' => '4', |
||
1645 | ], |
||
1646 | 'SIGN' => [ |
||
1647 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1648 | 'functionCall' => [MathTrig::class, 'SIGN'], |
||
1649 | 'argumentCount' => '1', |
||
1650 | ], |
||
1651 | 'SIN' => [ |
||
1652 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1653 | 'functionCall' => 'sin', |
||
1654 | 'argumentCount' => '1', |
||
1655 | ], |
||
1656 | 'SINH' => [ |
||
1657 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1658 | 'functionCall' => 'sinh', |
||
1659 | 'argumentCount' => '1', |
||
1660 | ], |
||
1661 | 'SKEW' => [ |
||
1662 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1663 | 'functionCall' => [Statistical::class, 'SKEW'], |
||
1664 | 'argumentCount' => '1+', |
||
1665 | ], |
||
1666 | 'SLN' => [ |
||
1667 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1668 | 'functionCall' => [Financial::class, 'SLN'], |
||
1669 | 'argumentCount' => '3', |
||
1670 | ], |
||
1671 | 'SLOPE' => [ |
||
1672 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1673 | 'functionCall' => [Statistical::class, 'SLOPE'], |
||
1674 | 'argumentCount' => '2', |
||
1675 | ], |
||
1676 | 'SMALL' => [ |
||
1677 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1678 | 'functionCall' => [Statistical::class, 'SMALL'], |
||
1679 | 'argumentCount' => '2', |
||
1680 | ], |
||
1681 | 'SQRT' => [ |
||
1682 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1683 | 'functionCall' => 'sqrt', |
||
1684 | 'argumentCount' => '1', |
||
1685 | ], |
||
1686 | 'SQRTPI' => [ |
||
1687 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1688 | 'functionCall' => [MathTrig::class, 'SQRTPI'], |
||
1689 | 'argumentCount' => '1', |
||
1690 | ], |
||
1691 | 'STANDARDIZE' => [ |
||
1692 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1693 | 'functionCall' => [Statistical::class, 'STANDARDIZE'], |
||
1694 | 'argumentCount' => '3', |
||
1695 | ], |
||
1696 | 'STDEV' => [ |
||
1697 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1698 | 'functionCall' => [Statistical::class, 'STDEV'], |
||
1699 | 'argumentCount' => '1+', |
||
1700 | ], |
||
1701 | 'STDEVA' => [ |
||
1702 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1703 | 'functionCall' => [Statistical::class, 'STDEVA'], |
||
1704 | 'argumentCount' => '1+', |
||
1705 | ], |
||
1706 | 'STDEVP' => [ |
||
1707 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1708 | 'functionCall' => [Statistical::class, 'STDEVP'], |
||
1709 | 'argumentCount' => '1+', |
||
1710 | ], |
||
1711 | 'STDEVPA' => [ |
||
1712 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1713 | 'functionCall' => [Statistical::class, 'STDEVPA'], |
||
1714 | 'argumentCount' => '1+', |
||
1715 | ], |
||
1716 | 'STEYX' => [ |
||
1717 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1718 | 'functionCall' => [Statistical::class, 'STEYX'], |
||
1719 | 'argumentCount' => '2', |
||
1720 | ], |
||
1721 | 'SUBSTITUTE' => [ |
||
1722 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1723 | 'functionCall' => [TextData::class, 'SUBSTITUTE'], |
||
1724 | 'argumentCount' => '3,4', |
||
1725 | ], |
||
1726 | 'SUBTOTAL' => [ |
||
1727 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1728 | 'functionCall' => [MathTrig::class, 'SUBTOTAL'], |
||
1729 | 'argumentCount' => '2+', |
||
1730 | 'passCellReference' => true, |
||
1731 | ], |
||
1732 | 'SUM' => [ |
||
1733 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1734 | 'functionCall' => [MathTrig::class, 'SUM'], |
||
1735 | 'argumentCount' => '1+', |
||
1736 | ], |
||
1737 | 'SUMIF' => [ |
||
1738 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1739 | 'functionCall' => [MathTrig::class, 'SUMIF'], |
||
1740 | 'argumentCount' => '2,3', |
||
1741 | ], |
||
1742 | 'SUMIFS' => [ |
||
1743 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1744 | 'functionCall' => [MathTrig::class, 'SUMIFS'], |
||
1745 | 'argumentCount' => '3+', |
||
1746 | ], |
||
1747 | 'SUMPRODUCT' => [ |
||
1748 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1749 | 'functionCall' => [MathTrig::class, 'SUMPRODUCT'], |
||
1750 | 'argumentCount' => '1+', |
||
1751 | ], |
||
1752 | 'SUMSQ' => [ |
||
1753 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1754 | 'functionCall' => [MathTrig::class, 'SUMSQ'], |
||
1755 | 'argumentCount' => '1+', |
||
1756 | ], |
||
1757 | 'SUMX2MY2' => [ |
||
1758 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1759 | 'functionCall' => [MathTrig::class, 'SUMX2MY2'], |
||
1760 | 'argumentCount' => '2', |
||
1761 | ], |
||
1762 | 'SUMX2PY2' => [ |
||
1763 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1764 | 'functionCall' => [MathTrig::class, 'SUMX2PY2'], |
||
1765 | 'argumentCount' => '2', |
||
1766 | ], |
||
1767 | 'SUMXMY2' => [ |
||
1768 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1769 | 'functionCall' => [MathTrig::class, 'SUMXMY2'], |
||
1770 | 'argumentCount' => '2', |
||
1771 | ], |
||
1772 | 'SYD' => [ |
||
1773 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1774 | 'functionCall' => [Financial::class, 'SYD'], |
||
1775 | 'argumentCount' => '4', |
||
1776 | ], |
||
1777 | 'T' => [ |
||
1778 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1779 | 'functionCall' => [TextData::class, 'RETURNSTRING'], |
||
1780 | 'argumentCount' => '1', |
||
1781 | ], |
||
1782 | 'TAN' => [ |
||
1783 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1784 | 'functionCall' => 'tan', |
||
1785 | 'argumentCount' => '1', |
||
1786 | ], |
||
1787 | 'TANH' => [ |
||
1788 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1789 | 'functionCall' => 'tanh', |
||
1790 | 'argumentCount' => '1', |
||
1791 | ], |
||
1792 | 'TBILLEQ' => [ |
||
1793 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1794 | 'functionCall' => [Financial::class, 'TBILLEQ'], |
||
1795 | 'argumentCount' => '3', |
||
1796 | ], |
||
1797 | 'TBILLPRICE' => [ |
||
1798 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1799 | 'functionCall' => [Financial::class, 'TBILLPRICE'], |
||
1800 | 'argumentCount' => '3', |
||
1801 | ], |
||
1802 | 'TBILLYIELD' => [ |
||
1803 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1804 | 'functionCall' => [Financial::class, 'TBILLYIELD'], |
||
1805 | 'argumentCount' => '3', |
||
1806 | ], |
||
1807 | 'TDIST' => [ |
||
1808 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1809 | 'functionCall' => [Statistical::class, 'TDIST'], |
||
1810 | 'argumentCount' => '3', |
||
1811 | ], |
||
1812 | 'TEXT' => [ |
||
1813 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1814 | 'functionCall' => [TextData::class, 'TEXTFORMAT'], |
||
1815 | 'argumentCount' => '2', |
||
1816 | ], |
||
1817 | 'TIME' => [ |
||
1818 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1819 | 'functionCall' => [DateTime::class, 'TIME'], |
||
1820 | 'argumentCount' => '3', |
||
1821 | ], |
||
1822 | 'TIMEVALUE' => [ |
||
1823 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1824 | 'functionCall' => [DateTime::class, 'TIMEVALUE'], |
||
1825 | 'argumentCount' => '1', |
||
1826 | ], |
||
1827 | 'TINV' => [ |
||
1828 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1829 | 'functionCall' => [Statistical::class, 'TINV'], |
||
1830 | 'argumentCount' => '2', |
||
1831 | ], |
||
1832 | 'TODAY' => [ |
||
1833 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1834 | 'functionCall' => [DateTime::class, 'DATENOW'], |
||
1835 | 'argumentCount' => '0', |
||
1836 | ], |
||
1837 | 'TRANSPOSE' => [ |
||
1838 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1839 | 'functionCall' => [LookupRef::class, 'TRANSPOSE'], |
||
1840 | 'argumentCount' => '1', |
||
1841 | ], |
||
1842 | 'TREND' => [ |
||
1843 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1844 | 'functionCall' => [Statistical::class, 'TREND'], |
||
1845 | 'argumentCount' => '1-4', |
||
1846 | ], |
||
1847 | 'TRIM' => [ |
||
1848 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1849 | 'functionCall' => [TextData::class, 'TRIMSPACES'], |
||
1850 | 'argumentCount' => '1', |
||
1851 | ], |
||
1852 | 'TRIMMEAN' => [ |
||
1853 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1854 | 'functionCall' => [Statistical::class, 'TRIMMEAN'], |
||
1855 | 'argumentCount' => '2', |
||
1856 | ], |
||
1857 | 'TRUE' => [ |
||
1858 | 'category' => Category::CATEGORY_LOGICAL, |
||
1859 | 'functionCall' => [Logical::class, 'TRUE'], |
||
1860 | 'argumentCount' => '0', |
||
1861 | ], |
||
1862 | 'TRUNC' => [ |
||
1863 | 'category' => Category::CATEGORY_MATH_AND_TRIG, |
||
1864 | 'functionCall' => [MathTrig::class, 'TRUNC'], |
||
1865 | 'argumentCount' => '1,2', |
||
1866 | ], |
||
1867 | 'TTEST' => [ |
||
1868 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1869 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1870 | 'argumentCount' => '4', |
||
1871 | ], |
||
1872 | 'TYPE' => [ |
||
1873 | 'category' => Category::CATEGORY_INFORMATION, |
||
1874 | 'functionCall' => [Functions::class, 'TYPE'], |
||
1875 | 'argumentCount' => '1', |
||
1876 | ], |
||
1877 | 'UPPER' => [ |
||
1878 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1879 | 'functionCall' => [TextData::class, 'UPPERCASE'], |
||
1880 | 'argumentCount' => '1', |
||
1881 | ], |
||
1882 | 'USDOLLAR' => [ |
||
1883 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1884 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1885 | 'argumentCount' => '2', |
||
1886 | ], |
||
1887 | 'VALUE' => [ |
||
1888 | 'category' => Category::CATEGORY_TEXT_AND_DATA, |
||
1889 | 'functionCall' => [TextData::class, 'VALUE'], |
||
1890 | 'argumentCount' => '1', |
||
1891 | ], |
||
1892 | 'VAR' => [ |
||
1893 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1894 | 'functionCall' => [Statistical::class, 'VARFunc'], |
||
1895 | 'argumentCount' => '1+', |
||
1896 | ], |
||
1897 | 'VARA' => [ |
||
1898 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1899 | 'functionCall' => [Statistical::class, 'VARA'], |
||
1900 | 'argumentCount' => '1+', |
||
1901 | ], |
||
1902 | 'VARP' => [ |
||
1903 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1904 | 'functionCall' => [Statistical::class, 'VARP'], |
||
1905 | 'argumentCount' => '1+', |
||
1906 | ], |
||
1907 | 'VARPA' => [ |
||
1908 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1909 | 'functionCall' => [Statistical::class, 'VARPA'], |
||
1910 | 'argumentCount' => '1+', |
||
1911 | ], |
||
1912 | 'VDB' => [ |
||
1913 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1914 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1915 | 'argumentCount' => '5-7', |
||
1916 | ], |
||
1917 | 'VLOOKUP' => [ |
||
1918 | 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, |
||
1919 | 'functionCall' => [LookupRef::class, 'VLOOKUP'], |
||
1920 | 'argumentCount' => '3,4', |
||
1921 | ], |
||
1922 | 'WEEKDAY' => [ |
||
1923 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1924 | 'functionCall' => [DateTime::class, 'WEEKDAY'], |
||
1925 | 'argumentCount' => '1,2', |
||
1926 | ], |
||
1927 | 'WEEKNUM' => [ |
||
1928 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1929 | 'functionCall' => [DateTime::class, 'WEEKNUM'], |
||
1930 | 'argumentCount' => '1,2', |
||
1931 | ], |
||
1932 | 'WEIBULL' => [ |
||
1933 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1934 | 'functionCall' => [Statistical::class, 'WEIBULL'], |
||
1935 | 'argumentCount' => '4', |
||
1936 | ], |
||
1937 | 'WORKDAY' => [ |
||
1938 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1939 | 'functionCall' => [DateTime::class, 'WORKDAY'], |
||
1940 | 'argumentCount' => '2+', |
||
1941 | ], |
||
1942 | 'XIRR' => [ |
||
1943 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1944 | 'functionCall' => [Financial::class, 'XIRR'], |
||
1945 | 'argumentCount' => '2,3', |
||
1946 | ], |
||
1947 | 'XNPV' => [ |
||
1948 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1949 | 'functionCall' => [Financial::class, 'XNPV'], |
||
1950 | 'argumentCount' => '3', |
||
1951 | ], |
||
1952 | 'YEAR' => [ |
||
1953 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1954 | 'functionCall' => [DateTime::class, 'YEAR'], |
||
1955 | 'argumentCount' => '1', |
||
1956 | ], |
||
1957 | 'YEARFRAC' => [ |
||
1958 | 'category' => Category::CATEGORY_DATE_AND_TIME, |
||
1959 | 'functionCall' => [DateTime::class, 'YEARFRAC'], |
||
1960 | 'argumentCount' => '2,3', |
||
1961 | ], |
||
1962 | 'YIELD' => [ |
||
1963 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1964 | 'functionCall' => [Functions::class, 'DUMMY'], |
||
1965 | 'argumentCount' => '6,7', |
||
1966 | ], |
||
1967 | 'YIELDDISC' => [ |
||
1968 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1969 | 'functionCall' => [Financial::class, 'YIELDDISC'], |
||
1970 | 'argumentCount' => '4,5', |
||
1971 | ], |
||
1972 | 'YIELDMAT' => [ |
||
1973 | 'category' => Category::CATEGORY_FINANCIAL, |
||
1974 | 'functionCall' => [Financial::class, 'YIELDMAT'], |
||
1975 | 'argumentCount' => '5,6', |
||
1976 | ], |
||
1977 | 'ZTEST' => [ |
||
1978 | 'category' => Category::CATEGORY_STATISTICAL, |
||
1979 | 'functionCall' => [Statistical::class, 'ZTEST'], |
||
1980 | 'argumentCount' => '2-3', |
||
1981 | ], |
||
1982 | ]; |
||
1983 | |||
1984 | // Internal functions used for special control purposes |
||
1985 | private static $controlFunctions = [ |
||
1986 | 'MKMATRIX' => [ |
||
1987 | 'argumentCount' => '*', |
||
1988 | 'functionCall' => 'self::mkMatrix', |
||
1989 | ], |
||
1990 | ]; |
||
1991 | |||
1992 | 137 | public function __construct(Spreadsheet $spreadsheet = null) |
|
2000 | |||
2001 | 1 | private static function loadLocales() |
|
2011 | |||
2012 | /** |
||
2013 | * Get an instance of this class. |
||
2014 | * |
||
2015 | * @param Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object, |
||
2016 | * or NULL to create a standalone claculation engine |
||
2017 | * |
||
2018 | * @return Calculation |
||
2019 | */ |
||
2020 | 519 | public static function getInstance(Spreadsheet $spreadsheet = null) |
|
2035 | |||
2036 | /** |
||
2037 | * Flush the calculation cache for any existing instance of this class |
||
2038 | * but only if a Calculation instance exists. |
||
2039 | */ |
||
2040 | public function flushInstance() |
||
2044 | |||
2045 | /** |
||
2046 | * Get the Logger for this calculation engine instance. |
||
2047 | * |
||
2048 | * @return Logger |
||
2049 | */ |
||
2050 | 60 | public function getDebugLog() |
|
2054 | |||
2055 | /** |
||
2056 | * __clone implementation. Cloning should not be allowed in a Singleton! |
||
2057 | * |
||
2058 | * @throws Exception |
||
2059 | */ |
||
2060 | final public function __clone() |
||
2064 | |||
2065 | /** |
||
2066 | * Return the locale-specific translation of TRUE. |
||
2067 | * |
||
2068 | * @return string locale-specific translation of TRUE |
||
2069 | */ |
||
2070 | 37 | public static function getTRUE() |
|
2074 | |||
2075 | /** |
||
2076 | * Return the locale-specific translation of FALSE. |
||
2077 | * |
||
2078 | * @return string locale-specific translation of FALSE |
||
2079 | */ |
||
2080 | 30 | public static function getFALSE() |
|
2084 | |||
2085 | /** |
||
2086 | * Set the Array Return Type (Array or Value of first element in the array). |
||
2087 | * |
||
2088 | * @param string $returnType Array return type |
||
2089 | * |
||
2090 | * @return bool Success or failure |
||
2091 | */ |
||
2092 | 19 | public static function setArrayReturnType($returnType) |
|
2104 | |||
2105 | /** |
||
2106 | * Return the Array Return Type (Array or Value of first element in the array). |
||
2107 | * |
||
2108 | * @return string $returnType Array return type |
||
2109 | */ |
||
2110 | 8 | public static function getArrayReturnType() |
|
2114 | |||
2115 | /** |
||
2116 | * Is calculation caching enabled? |
||
2117 | * |
||
2118 | * @return bool |
||
2119 | */ |
||
2120 | 1 | public function getCalculationCacheEnabled() |
|
2121 | { |
||
2122 | 1 | return $this->calculationCacheEnabled; |
|
2123 | } |
||
2124 | |||
2125 | /** |
||
2126 | * Enable/disable calculation cache. |
||
2127 | * |
||
2128 | * @param bool $pValue |
||
2129 | */ |
||
2130 | public function setCalculationCacheEnabled($pValue) |
||
2135 | |||
2136 | /** |
||
2137 | * Enable calculation cache. |
||
2138 | */ |
||
2139 | public function enableCalculationCache() |
||
2143 | |||
2144 | /** |
||
2145 | * Disable calculation cache. |
||
2146 | */ |
||
2147 | public function disableCalculationCache() |
||
2151 | |||
2152 | /** |
||
2153 | * Clear calculation cache. |
||
2154 | */ |
||
2155 | public function clearCalculationCache() |
||
2159 | |||
2160 | /** |
||
2161 | * Clear calculation cache for a specified worksheet. |
||
2162 | * |
||
2163 | * @param string $worksheetName |
||
2164 | */ |
||
2165 | 2 | public function clearCalculationCacheForWorksheet($worksheetName) |
|
2171 | |||
2172 | /** |
||
2173 | * Rename calculation cache for a specified worksheet. |
||
2174 | * |
||
2175 | * @param string $fromWorksheetName |
||
2176 | * @param string $toWorksheetName |
||
2177 | */ |
||
2178 | 137 | public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName) |
|
2185 | |||
2186 | /** |
||
2187 | * Get the currently defined locale code. |
||
2188 | * |
||
2189 | * @return string |
||
2190 | */ |
||
2191 | public function getLocale() |
||
2195 | |||
2196 | /** |
||
2197 | * Set the locale code. |
||
2198 | * |
||
2199 | * @param string $locale The locale to use for formula translation, eg: 'en_us' |
||
2200 | * |
||
2201 | * @return bool |
||
2202 | */ |
||
2203 | 426 | public function setLocale($locale) |
|
2283 | |||
2284 | /** |
||
2285 | * @param string $fromSeparator |
||
2286 | * @param string $toSeparator |
||
2287 | * @param string $formula |
||
2288 | * @param bool $inBraces |
||
2289 | * |
||
2290 | * @return string |
||
2291 | */ |
||
2292 | 5 | public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces) |
|
2315 | |||
2316 | /** |
||
2317 | * @param string $fromSeparator |
||
2318 | * @param string $toSeparator |
||
2319 | * @param mixed $from |
||
2320 | * @param mixed $to |
||
2321 | * @param mixed $formula |
||
2322 | */ |
||
2323 | private static function translateFormula($from, $to, $formula, $fromSeparator, $toSeparator) |
||
2353 | |||
2354 | private static $functionReplaceFromExcel = null; |
||
2355 | private static $functionReplaceToLocale = null; |
||
2356 | |||
2357 | View Code Duplication | public function _translateFormulaToLocale($formula) |
|
2381 | |||
2382 | private static $functionReplaceFromLocale = null; |
||
2383 | private static $functionReplaceToExcel = null; |
||
2384 | |||
2385 | View Code Duplication | public function _translateFormulaToEnglish($formula) |
|
2409 | |||
2410 | 46 | public static function localeFunc($function) |
|
2425 | |||
2426 | /** |
||
2427 | * Wrap string values in quotes. |
||
2428 | * |
||
2429 | * @param mixed $value |
||
2430 | * |
||
2431 | * @return mixed |
||
2432 | */ |
||
2433 | 80 | public static function wrapResult($value) |
|
2450 | |||
2451 | /** |
||
2452 | * Remove quotes used as a wrapper to identify string values. |
||
2453 | * |
||
2454 | * @param mixed $value |
||
2455 | * |
||
2456 | * @return mixed |
||
2457 | */ |
||
2458 | 94 | public static function unwrapResult($value) |
|
2471 | |||
2472 | /** |
||
2473 | * Calculate cell value (using formula from a cell ID) |
||
2474 | * Retained for backward compatibility. |
||
2475 | * |
||
2476 | * @param Cell $pCell Cell to calculate |
||
2477 | * |
||
2478 | * @throws Exception |
||
2479 | * |
||
2480 | * @return mixed |
||
2481 | */ |
||
2482 | public function calculate(Cell $pCell = null) |
||
2490 | |||
2491 | /** |
||
2492 | * Calculate the value of a cell formula. |
||
2493 | * |
||
2494 | * @param Cell $pCell Cell to calculate |
||
2495 | * @param bool $resetLog Flag indicating whether the debug log should be reset or not |
||
2496 | * |
||
2497 | * @throws Exception |
||
2498 | * |
||
2499 | * @return mixed |
||
2500 | */ |
||
2501 | 48 | public function calculateCellValue(Cell $pCell = null, $resetLog = true) |
|
2569 | |||
2570 | /** |
||
2571 | * Validate and parse a formula string. |
||
2572 | * |
||
2573 | * @param string $formula Formula to parse |
||
2574 | * |
||
2575 | * @throws Exception |
||
2576 | * |
||
2577 | * @return array |
||
2578 | */ |
||
2579 | public function parseFormula($formula) |
||
2595 | |||
2596 | /** |
||
2597 | * Calculate the value of a formula. |
||
2598 | * |
||
2599 | * @param string $formula Formula to parse |
||
2600 | * @param string $cellID Address of the cell to calculate |
||
2601 | * @param Cell $pCell Cell to calculate |
||
2602 | * |
||
2603 | * @throws Exception |
||
2604 | * |
||
2605 | * @return mixed |
||
2606 | */ |
||
2607 | 1 | public function calculateFormula($formula, $cellID = null, Cell $pCell = null) |
|
2608 | { |
||
2609 | // Initialise the logging settings |
||
2610 | 1 | $this->formulaError = null; |
|
2611 | 1 | $this->debugLog->clearLog(); |
|
2612 | 1 | $this->cyclicReferenceStack->clear(); |
|
2613 | |||
2614 | 1 | if ($this->spreadsheet !== null && $cellID === null && $pCell === null) { |
|
2615 | $cellID = 'A1'; |
||
2616 | $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID); |
||
2617 | } else { |
||
2618 | // Disable calculation cacheing because it only applies to cell calculations, not straight formulae |
||
2619 | // But don't actually flush any cache |
||
2620 | 1 | $resetCache = $this->getCalculationCacheEnabled(); |
|
2621 | 1 | $this->calculationCacheEnabled = false; |
|
2622 | } |
||
2623 | |||
2624 | // Execute the calculation |
||
2625 | try { |
||
2626 | 1 | $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell)); |
|
2627 | 1 | } catch (\Exception $e) { |
|
2628 | 1 | throw new Exception($e->getMessage()); |
|
2629 | } |
||
2630 | |||
2631 | 1 | if ($this->spreadsheet === null) { |
|
2632 | // Reset calculation cacheing to its previous state |
||
2633 | $this->calculationCacheEnabled = $resetCache; |
||
2634 | } |
||
2635 | |||
2636 | 1 | return $result; |
|
2637 | } |
||
2638 | |||
2639 | /** |
||
2640 | * @param string $cellReference |
||
2641 | * @param mixed $cellValue |
||
2642 | * |
||
2643 | * @return bool |
||
2644 | */ |
||
2645 | 49 | public function getValueFromCache($cellReference, &$cellValue) |
|
2660 | |||
2661 | /** |
||
2662 | * @param string $cellReference |
||
2663 | * @param mixed $cellValue |
||
2664 | */ |
||
2665 | 49 | public function saveValueToCache($cellReference, $cellValue) |
|
2671 | |||
2672 | /** |
||
2673 | * Parse a cell formula and calculate its value. |
||
2674 | * |
||
2675 | * @param string $formula The formula to parse and calculate |
||
2676 | * @param string $cellID The ID (e.g. A3) of the cell that we are calculating |
||
2677 | * @param Cell $pCell Cell to calculate |
||
2678 | * |
||
2679 | * @throws Exception |
||
2680 | * |
||
2681 | * @return mixed |
||
2682 | */ |
||
2683 | 118 | public function _calculateFormulaValue($formula, $cellID = null, Cell $pCell = null) |
|
2739 | |||
2740 | /** |
||
2741 | * Ensure that paired matrix operands are both matrices and of the same size. |
||
2742 | * |
||
2743 | * @param mixed &$operand1 First matrix operand |
||
2744 | * @param mixed &$operand2 Second matrix operand |
||
2745 | * @param int $resize Flag indicating whether the matrices should be resized to match |
||
2746 | * and (if so), whether the smaller dimension should grow or the |
||
2747 | * larger should shrink. |
||
2748 | * 0 = no resize |
||
2749 | * 1 = shrink to fit |
||
2750 | * 2 = extend to fit |
||
2751 | */ |
||
2752 | 5 | private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1) |
|
2782 | |||
2783 | /** |
||
2784 | * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0. |
||
2785 | * |
||
2786 | * @param mixed &$matrix matrix operand |
||
2787 | * |
||
2788 | * @return int[] An array comprising the number of rows, and number of columns |
||
2789 | */ |
||
2790 | 5 | private static function getMatrixDimensions(&$matrix) |
|
2806 | |||
2807 | /** |
||
2808 | * Ensure that paired matrix operands are both matrices of the same size. |
||
2809 | * |
||
2810 | * @param mixed &$matrix1 First matrix operand |
||
2811 | * @param mixed &$matrix2 Second matrix operand |
||
2812 | * @param int $matrix1Rows Row size of first matrix operand |
||
2813 | * @param int $matrix1Columns Column size of first matrix operand |
||
2814 | * @param int $matrix2Rows Row size of second matrix operand |
||
2815 | * @param int $matrix2Columns Column size of second matrix operand |
||
2816 | */ |
||
2817 | 5 | private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns) |
|
2849 | |||
2850 | /** |
||
2851 | * Ensure that paired matrix operands are both matrices of the same size. |
||
2852 | * |
||
2853 | * @param mixed &$matrix1 First matrix operand |
||
2854 | * @param mixed &$matrix2 Second matrix operand |
||
2855 | * @param int $matrix1Rows Row size of first matrix operand |
||
2856 | * @param int $matrix1Columns Column size of first matrix operand |
||
2857 | * @param int $matrix2Rows Row size of second matrix operand |
||
2858 | * @param int $matrix2Columns Column size of second matrix operand |
||
2859 | */ |
||
2860 | private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns) |
||
2896 | |||
2897 | /** |
||
2898 | * Format details of an operand for display in the log (based on operand type). |
||
2899 | * |
||
2900 | * @param mixed $value First matrix operand |
||
2901 | * |
||
2902 | * @return mixed |
||
2903 | */ |
||
2904 | 117 | private function showValue($value) |
|
2934 | |||
2935 | /** |
||
2936 | * Format type and details of an operand for display in the log (based on operand type). |
||
2937 | * |
||
2938 | * @param mixed $value First matrix operand |
||
2939 | * |
||
2940 | * @return null|string |
||
2941 | */ |
||
2942 | 117 | private function showTypeDetails($value) |
|
2972 | |||
2973 | /** |
||
2974 | * @param string $formula |
||
2975 | * |
||
2976 | * @return string |
||
2977 | */ |
||
2978 | 118 | private function convertMatrixReferences($formula) |
|
3028 | |||
3029 | private static function mkMatrix(...$args) |
||
3033 | |||
3034 | // Binary Operators |
||
3035 | // These operators always work on two values |
||
3036 | // Array key is the operator, the value indicates whether this is a left or right associative operator |
||
3037 | private static $operatorAssociativity = [ |
||
3038 | '^' => 0, // Exponentiation |
||
3039 | '*' => 0, '/' => 0, // Multiplication and Division |
||
3040 | '+' => 0, '-' => 0, // Addition and Subtraction |
||
3041 | '&' => 0, // Concatenation |
||
3042 | '|' => 0, ':' => 0, // Intersect and Range |
||
3043 | '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, // Comparison |
||
3044 | ]; |
||
3045 | |||
3046 | // Comparison (Boolean) Operators |
||
3047 | // These operators work on two values, but always return a boolean result |
||
3048 | private static $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true]; |
||
3049 | |||
3050 | // Operator Precedence |
||
3051 | // This list includes all valid operators, whether binary (including boolean) or unary (such as %) |
||
3052 | // Array key is the operator, the value is its precedence |
||
3053 | private static $operatorPrecedence = [ |
||
3054 | ':' => 8, // Range |
||
3055 | '|' => 7, // Intersect |
||
3056 | '~' => 6, // Negation |
||
3057 | '%' => 5, // Percentage |
||
3058 | '^' => 4, // Exponentiation |
||
3059 | '*' => 3, '/' => 3, // Multiplication and Division |
||
3060 | '+' => 2, '-' => 2, // Addition and Subtraction |
||
3061 | '&' => 1, // Concatenation |
||
3062 | '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, // Comparison |
||
3063 | ]; |
||
3064 | |||
3065 | // Convert infix to postfix notation |
||
3066 | |||
3067 | /** |
||
3068 | * @param string $formula |
||
3069 | * @param null|\PhpOffice\PhpSpreadsheet\Cell\Cell $pCell |
||
3070 | * |
||
3071 | * @return bool |
||
3072 | */ |
||
3073 | 118 | private function _parseFormula($formula, Cell $pCell = null) |
|
3074 | { |
||
3075 | 118 | if (($formula = $this->convertMatrixReferences(trim($formula))) === false) { |
|
3076 | return false; |
||
3077 | } |
||
3078 | |||
3079 | // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet), |
||
3080 | // so we store the parent worksheet so that we can re-attach it when necessary |
||
3081 | 118 | $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null; |
|
3082 | |||
3083 | 118 | $regexpMatchString = '/^(' . self::CALCULATION_REGEXP_FUNCTION . |
|
3084 | 118 | '|' . self::CALCULATION_REGEXP_CELLREF . |
|
3085 | 118 | '|' . self::CALCULATION_REGEXP_NUMBER . |
|
3086 | 118 | '|' . self::CALCULATION_REGEXP_STRING . |
|
3087 | 118 | '|' . self::CALCULATION_REGEXP_OPENBRACE . |
|
3088 | 118 | '|' . self::CALCULATION_REGEXP_NAMEDRANGE . |
|
3089 | 118 | '|' . self::CALCULATION_REGEXP_ERROR . |
|
3090 | 118 | ')/si'; |
|
3091 | |||
3092 | // Start with initialisation |
||
3093 | 118 | $index = 0; |
|
3094 | 118 | $stack = new Stack(); |
|
3095 | 118 | $output = []; |
|
3096 | 118 | $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a |
|
3097 | // - is a negation or + is a positive operator rather than an operation |
||
3098 | 118 | $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand |
|
3099 | // should be null in a function call |
||
3100 | // The guts of the lexical parser |
||
3101 | // Loop through the formula extracting each operator and operand in turn |
||
3102 | 118 | while (true) { |
|
3103 | 118 | $opCharacter = $formula[$index]; // Get the first character of the value at the current index position |
|
3104 | 118 | if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) { |
|
3105 | 34 | $opCharacter .= $formula[++$index]; |
|
3106 | } |
||
3107 | |||
3108 | // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand |
||
3109 | 118 | $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match); |
|
3110 | |||
3111 | 118 | if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus? |
|
3112 | 2 | $stack->push('Unary Operator', '~'); // Put a negation on the stack |
|
3113 | 2 | ++$index; // and drop the negation symbol |
|
3114 | 118 | } elseif ($opCharacter == '%' && $expectingOperator) { |
|
3115 | $stack->push('Unary Operator', '%'); // Put a percentage on the stack |
||
3116 | ++$index; |
||
3117 | 118 | } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded? |
|
3118 | ++$index; // Drop the redundant plus symbol |
||
3119 | 118 | } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal |
|
3120 | return $this->raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression |
||
3121 | 118 | } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack? |
|
3122 | 111 | View Code Duplication | while ($stack->count() > 0 && |
3123 | 111 | ($o2 = $stack->last()) && |
|
3124 | 111 | isset(self::$operators[$o2['value']]) && |
|
3125 | 111 | @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) { |
|
3126 | 2 | $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output |
|
3127 | } |
||
3128 | 111 | $stack->push('Binary Operator', $opCharacter); // Finally put our current operator onto the stack |
|
3129 | 111 | ++$index; |
|
3130 | 111 | $expectingOperator = false; |
|
3131 | 118 | } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis? |
|
3132 | 46 | $expectingOperand = false; |
|
3133 | 46 | View Code Duplication | while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last ( |
3134 | 39 | if ($o2 === null) { |
|
3135 | return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"'); |
||
3136 | } |
||
3137 | 39 | $output[] = $o2; |
|
3138 | } |
||
3139 | 46 | $d = $stack->last(2); |
|
3140 | 46 | if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function? |
|
3141 | 46 | $functionName = $matches[1]; // Get the function name |
|
3142 | 46 | $d = $stack->pop(); |
|
3143 | 46 | $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack) |
|
3144 | 46 | $output[] = $d; // Dump the argument count on the output |
|
3145 | 46 | $output[] = $stack->pop(); // Pop the function and push onto the output |
|
3146 | 46 | if (isset(self::$controlFunctions[$functionName])) { |
|
3147 | $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount']; |
||
3148 | $functionCall = self::$controlFunctions[$functionName]['functionCall']; |
||
3149 | 46 | } elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) { |
|
3150 | 46 | $expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount']; |
|
3151 | 46 | $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall']; |
|
3152 | } else { // did we somehow push a non-function on the stack? this should never happen |
||
3153 | return $this->raiseFormulaError('Formula Error: Internal error, non-function on stack'); |
||
3154 | } |
||
3155 | // Check the argument count |
||
3156 | 46 | $argumentCountError = false; |
|
3157 | 46 | if (is_numeric($expectedArgumentCount)) { |
|
3158 | 25 | if ($expectedArgumentCount < 0) { |
|
3159 | if ($argumentCount > abs($expectedArgumentCount)) { |
||
3160 | $argumentCountError = true; |
||
3161 | $expectedArgumentCountString = 'no more than ' . abs($expectedArgumentCount); |
||
3162 | } |
||
3163 | } else { |
||
3164 | 25 | if ($argumentCount != $expectedArgumentCount) { |
|
3165 | $argumentCountError = true; |
||
3166 | 25 | $expectedArgumentCountString = $expectedArgumentCount; |
|
3167 | } |
||
3168 | } |
||
3169 | 40 | } elseif ($expectedArgumentCount != '*') { |
|
3170 | 40 | $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch); |
|
3171 | 40 | switch ($argMatch[2]) { |
|
3172 | 40 | case '+': |
|
3173 | 38 | if ($argumentCount < $argMatch[1]) { |
|
3174 | $argumentCountError = true; |
||
3175 | $expectedArgumentCountString = $argMatch[1] . ' or more '; |
||
3176 | } |
||
3177 | |||
3178 | 38 | break; |
|
3179 | 16 | View Code Duplication | case '-': |
3180 | 13 | if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) { |
|
3181 | $argumentCountError = true; |
||
3182 | $expectedArgumentCountString = 'between ' . $argMatch[1] . ' and ' . $argMatch[3]; |
||
3183 | } |
||
3184 | |||
3185 | 13 | break; |
|
3186 | 3 | View Code Duplication | case ',': |
3187 | 3 | if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) { |
|
3188 | $argumentCountError = true; |
||
3189 | $expectedArgumentCountString = 'either ' . $argMatch[1] . ' or ' . $argMatch[3]; |
||
3190 | } |
||
3191 | |||
3192 | 3 | break; |
|
3193 | } |
||
3194 | } |
||
3195 | 46 | if ($argumentCountError) { |
|
3196 | return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, " . $expectedArgumentCountString . ' expected'); |
||
3197 | } |
||
3198 | } |
||
3199 | 46 | ++$index; |
|
3200 | 118 | } elseif ($opCharacter == ',') { // Is this the separator for function arguments? |
|
3201 | 30 | View Code Duplication | while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last ( |
3202 | 25 | if ($o2 === null) { |
|
3203 | return $this->raiseFormulaError('Formula Error: Unexpected ,'); |
||
3204 | } |
||
3205 | 25 | $output[] = $o2; // pop the argument expression stuff and push onto the output |
|
3206 | } |
||
3207 | // If we've a comma when we're expecting an operand, then what we actually have is a null operand; |
||
3208 | // so push a null onto the stack |
||
3209 | 30 | if (($expectingOperand) || (!$expectingOperator)) { |
|
3210 | $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null]; |
||
3211 | } |
||
3212 | // make sure there was a function |
||
3213 | 30 | $d = $stack->last(2); |
|
3214 | 30 | if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { |
|
3215 | return $this->raiseFormulaError('Formula Error: Unexpected ,'); |
||
3216 | } |
||
3217 | 30 | $d = $stack->pop(); |
|
3218 | 30 | $stack->push($d['type'], ++$d['value'], $d['reference']); // increment the argument count |
|
3219 | 30 | $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again |
|
3220 | 30 | $expectingOperator = false; |
|
3221 | 30 | $expectingOperand = true; |
|
3222 | 30 | ++$index; |
|
3223 | 118 | } elseif ($opCharacter == '(' && !$expectingOperator) { |
|
3224 | 3 | $stack->push('Brace', '('); |
|
3225 | 3 | ++$index; |
|
3226 | 118 | } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number? |
|
3227 | 118 | $expectingOperator = true; |
|
3228 | 118 | $expectingOperand = false; |
|
3229 | 118 | $val = $match[1]; |
|
3230 | 118 | $length = strlen($val); |
|
3231 | |||
3232 | 118 | if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) { |
|
3233 | 46 | $val = preg_replace('/\s/u', '', $val); |
|
3234 | 46 | if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function |
|
3235 | 46 | $stack->push('Function', strtoupper($val)); |
|
3236 | 46 | $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index + $length), $amatch); |
|
3237 | 46 | if ($ax) { |
|
3238 | 9 | $stack->push('Operand Count for Function ' . strtoupper($val) . ')', 0); |
|
3239 | 9 | $expectingOperator = true; |
|
3240 | } else { |
||
3241 | 46 | $stack->push('Operand Count for Function ' . strtoupper($val) . ')', 1); |
|
3242 | 46 | $expectingOperator = false; |
|
3243 | } |
||
3244 | 46 | $stack->push('Brace', '('); |
|
3245 | } else { // it's a var w/ implicit multiplication |
||
3246 | 46 | $output[] = ['type' => 'Value', 'value' => $matches[1], 'reference' => null]; |
|
3247 | } |
||
3248 | 118 | } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $val, $matches)) { |
|
3249 | // Watch for this case-change when modifying to allow cell references in different worksheets... |
||
3250 | // Should only be applied to the actual cell column, not the worksheet name |
||
3251 | |||
3252 | // If the last entry on the stack was a : operator, then we have a cell range reference |
||
3253 | 60 | $testPrevOp = $stack->last(1); |
|
3254 | 60 | if ($testPrevOp['value'] == ':') { |
|
3255 | // If we have a worksheet reference, then we're playing with a 3D reference |
||
3256 | 52 | if ($matches[2] == '') { |
|
3257 | // Otherwise, we 'inherit' the worksheet reference from the start cell reference |
||
3258 | // The start of the cell range reference should be the last entry in $output |
||
3259 | 52 | $startCellRef = $output[count($output) - 1]['value']; |
|
3260 | 52 | preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $startCellRef, $startMatches); |
|
3261 | 52 | if ($startMatches[2] > '') { |
|
3262 | 52 | $val = $startMatches[2] . '!' . $val; |
|
3263 | } |
||
3264 | } else { |
||
3265 | return $this->raiseFormulaError('3D Range references are not yet supported'); |
||
3266 | } |
||
3267 | } |
||
3268 | |||
3269 | 60 | $output[] = ['type' => 'Cell Reference', 'value' => $val, 'reference' => $val]; |
|
3270 | } else { // it's a variable, constant, string, number or boolean |
||
3271 | // If the last entry on the stack was a : operator, then we may have a row or column range reference |
||
3272 | 95 | $testPrevOp = $stack->last(1); |
|
3273 | 95 | if ($testPrevOp['value'] == ':') { |
|
3274 | 1 | $startRowColRef = $output[count($output) - 1]['value']; |
|
3275 | 1 | $rangeWS1 = ''; |
|
3276 | 1 | if (strpos('!', $startRowColRef) !== false) { |
|
3277 | list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef); |
||
3278 | } |
||
3279 | 1 | if ($rangeWS1 != '') { |
|
3280 | $rangeWS1 .= '!'; |
||
3281 | } |
||
3282 | 1 | $rangeWS2 = $rangeWS1; |
|
3283 | 1 | if (strpos('!', $val) !== false) { |
|
3284 | list($rangeWS2, $val) = explode('!', $val); |
||
3285 | } |
||
3286 | 1 | if ($rangeWS2 != '') { |
|
3287 | $rangeWS2 .= '!'; |
||
3288 | } |
||
3289 | 1 | if ((is_int($startRowColRef)) && (ctype_digit($val)) && |
|
3290 | 1 | ($startRowColRef <= 1048576) && ($val <= 1048576)) { |
|
3291 | // Row range |
||
3292 | $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007 |
||
3293 | $output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef; |
||
3294 | $val = $rangeWS2 . $endRowColRef . $val; |
||
3295 | 1 | } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) && |
|
3296 | 1 | (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) { |
|
3297 | // Column range |
||
3298 | $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007 |
||
3299 | $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1'; |
||
3300 | $val = $rangeWS2 . $val . $endRowColRef; |
||
3301 | } |
||
3302 | } |
||
3303 | |||
3304 | 95 | $localeConstant = false; |
|
3305 | 95 | if ($opCharacter == '"') { |
|
3306 | // UnEscape any quotes within the string |
||
3307 | 59 | $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val))); |
|
3308 | 62 | } elseif (is_numeric($val)) { |
|
3309 | 60 | if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { |
|
3310 | 21 | $val = (float) $val; |
|
3311 | } else { |
||
3312 | 60 | $val = (int) $val; |
|
3313 | } |
||
3314 | 6 | } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) { |
|
3315 | 1 | $excelConstant = trim(strtoupper($val)); |
|
3316 | 1 | $val = self::$excelConstants[$excelConstant]; |
|
3317 | 5 | } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) { |
|
3318 | $val = self::$excelConstants[$localeConstant]; |
||
3319 | } |
||
3320 | 95 | $details = ['type' => 'Value', 'value' => $val, 'reference' => null]; |
|
3321 | 95 | if ($localeConstant) { |
|
3322 | $details['localeValue'] = $localeConstant; |
||
3323 | } |
||
3324 | 95 | $output[] = $details; |
|
3325 | } |
||
3326 | 118 | $index += $length; |
|
3327 | } elseif ($opCharacter == '$') { // absolute row or column range |
||
3328 | ++$index; |
||
3329 | } elseif ($opCharacter == ')') { // miscellaneous error checking |
||
3330 | if ($expectingOperand) { |
||
3331 | $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null]; |
||
3332 | $expectingOperand = false; |
||
3333 | $expectingOperator = true; |
||
3334 | } else { |
||
3335 | return $this->raiseFormulaError("Formula Error: Unexpected ')'"); |
||
3336 | } |
||
3337 | } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) { |
||
3338 | return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'"); |
||
3339 | } else { // I don't even want to know what you did to get here |
||
3340 | return $this->raiseFormulaError('Formula Error: An unexpected error occured'); |
||
3341 | } |
||
3342 | // Test for end of formula string |
||
3343 | 118 | if ($index == strlen($formula)) { |
|
3344 | // Did we end with an operator?. |
||
3345 | // Only valid for the % unary operator |
||
3346 | 118 | if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) { |
|
3347 | return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands"); |
||
3348 | } |
||
3349 | |||
3350 | 118 | break; |
|
3351 | } |
||
3352 | // Ignore white space |
||
3353 | 117 | while (($formula[$index] == "\n") || ($formula[$index] == "\r")) { |
|
3354 | ++$index; |
||
3355 | } |
||
3356 | 117 | if ($formula[$index] == ' ') { |
|
3357 | 55 | while ($formula[$index] == ' ') { |
|
3358 | 55 | ++$index; |
|
3359 | } |
||
3360 | // If we're expecting an operator, but only have a space between the previous and next operands (and both are |
||
3361 | // Cell References) then we have an INTERSECTION operator |
||
3362 | 55 | if (($expectingOperator) && (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) && |
|
3363 | 55 | ($output[count($output) - 1]['type'] == 'Cell Reference')) { |
|
3364 | View Code Duplication | while ($stack->count() > 0 && |
|
3365 | ($o2 = $stack->last()) && |
||
3366 | isset(self::$operators[$o2['value']]) && |
||
3367 | @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) { |
||
3368 | $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output |
||
3369 | } |
||
3370 | $stack->push('Binary Operator', '|'); // Put an Intersect Operator on the stack |
||
3371 | $expectingOperator = false; |
||
3372 | } |
||
3373 | } |
||
3374 | } |
||
3375 | |||
3376 | 118 | View Code Duplication | while (($op = $stack->pop()) !== null) { // pop everything off the stack and push onto output |
3377 | 98 | if ((is_array($op) && $op['value'] == '(') || ($op === '(')) { |
|
3378 | return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced |
||
3379 | } |
||
3380 | 98 | $output[] = $op; |
|
3381 | } |
||
3382 | |||
3383 | 118 | return $output; |
|
3384 | } |
||
3385 | |||
3386 | 111 | private static function dataTestReference(&$operandData) |
|
3401 | |||
3402 | // evaluate postfix notation |
||
3403 | |||
3404 | /** |
||
3405 | * @param mixed $tokens |
||
3406 | * @param null|string $cellID |
||
3407 | * @param null|Cell $pCell |
||
3408 | * |
||
3409 | * @return bool |
||
3410 | */ |
||
3411 | 118 | private function processTokenStack($tokens, $cellID = null, Cell $pCell = null) |
|
3412 | { |
||
3413 | 118 | if ($tokens == false) { |
|
3414 | return false; |
||
3415 | } |
||
3416 | |||
3417 | // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection), |
||
3418 | // so we store the parent cell collection so that we can re-attach it when necessary |
||
3419 | 118 | $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null; |
|
3420 | 118 | $pCellParent = ($pCell !== null) ? $pCell->getParent() : null; |
|
3421 | 118 | $stack = new Stack(); |
|
3422 | |||
3423 | // Loop through each token in turn |
||
3424 | 118 | foreach ($tokens as $tokenData) { |
|
3425 | 118 | $token = $tokenData['value']; |
|
3426 | // 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 |
||
3427 | 118 | if (isset(self::$binaryOperators[$token])) { |
|
3428 | // We must have two operands, error if we don't |
||
3429 | 111 | if (($operand2Data = $stack->pop()) === null) { |
|
3430 | return $this->raiseFormulaError('Internal error - Operand value missing from stack'); |
||
3431 | } |
||
3432 | 111 | if (($operand1Data = $stack->pop()) === null) { |
|
3433 | return $this->raiseFormulaError('Internal error - Operand value missing from stack'); |
||
3434 | } |
||
3435 | |||
3436 | 111 | $operand1 = self::dataTestReference($operand1Data); |
|
3437 | 111 | $operand2 = self::dataTestReference($operand2Data); |
|
3438 | |||
3439 | // Log what we're doing |
||
3440 | 111 | if ($token == ':') { |
|
3441 | 52 | $this->debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference'])); |
|
3442 | } else { |
||
3443 | 94 | $this->debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2)); |
|
3444 | } |
||
3445 | |||
3446 | // Process the operation in the appropriate manner |
||
3447 | switch ($token) { |
||
3448 | // Comparison (Boolean) Operators |
||
3449 | 111 | case '>': // Greater than |
|
3450 | 97 | case '<': // Less than |
|
3451 | 90 | case '>=': // Greater than or Equal to |
|
3452 | 82 | case '<=': // Less than or Equal to |
|
3453 | 75 | case '=': // Equality |
|
3454 | 66 | case '<>': // Inequality |
|
3455 | 74 | $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack); |
|
3456 | |||
3457 | 74 | break; |
|
3458 | // Binary Operators |
||
3459 | 59 | case ':': // Range |
|
3460 | 52 | $sheet1 = $sheet2 = ''; |
|
3461 | 52 | if (strpos($operand1Data['reference'], '!') !== false) { |
|
3462 | 52 | list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']); |
|
3463 | } else { |
||
3464 | 1 | $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : ''; |
|
3465 | } |
||
3466 | 52 | if (strpos($operand2Data['reference'], '!') !== false) { |
|
3467 | 52 | list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']); |
|
3468 | } else { |
||
3469 | 10 | $sheet2 = $sheet1; |
|
3470 | } |
||
3471 | 52 | if ($sheet1 == $sheet2) { |
|
3472 | 52 | View Code Duplication | if ($operand1Data['reference'] === null) { |
3473 | if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) { |
||
3474 | $operand1Data['reference'] = $pCell->getColumn() . $operand1Data['value']; |
||
3475 | } elseif (trim($operand1Data['reference']) == '') { |
||
3476 | $operand1Data['reference'] = $pCell->getCoordinate(); |
||
3477 | } else { |
||
3478 | $operand1Data['reference'] = $operand1Data['value'] . $pCell->getRow(); |
||
3479 | } |
||
3480 | } |
||
3481 | 52 | View Code Duplication | if ($operand2Data['reference'] === null) { |
3482 | if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) { |
||
3483 | $operand2Data['reference'] = $pCell->getColumn() . $operand2Data['value']; |
||
3484 | } elseif (trim($operand2Data['reference']) == '') { |
||
3485 | $operand2Data['reference'] = $pCell->getCoordinate(); |
||
3486 | } else { |
||
3487 | $operand2Data['reference'] = $operand2Data['value'] . $pCell->getRow(); |
||
3488 | } |
||
3489 | } |
||
3490 | |||
3491 | 52 | $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference'])); |
|
3492 | 52 | $oCol = $oRow = []; |
|
3493 | 52 | foreach ($oData as $oDatum) { |
|
3494 | 52 | $oCR = Cell::coordinateFromString($oDatum); |
|
3495 | 52 | $oCol[] = Cell::columnIndexFromString($oCR[0]) - 1; |
|
3496 | 52 | $oRow[] = $oCR[1]; |
|
3497 | } |
||
3498 | 52 | $cellRef = Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . Cell::stringFromColumnIndex(max($oCol)) . max($oRow); |
|
3499 | 52 | View Code Duplication | if ($pCellParent !== null) { |
3500 | 52 | $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false); |
|
3501 | } else { |
||
3502 | return $this->raiseFormulaError('Unable to access Cell Reference'); |
||
3503 | } |
||
3504 | 52 | $stack->push('Cell Reference', $cellValue, $cellRef); |
|
3505 | } else { |
||
3506 | $stack->push('Error', Functions::REF(), null); |
||
3507 | } |
||
3508 | |||
3509 | 52 | break; |
|
3510 | 32 | case '+': // Addition |
|
3511 | 23 | $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack); |
|
3512 | |||
3513 | 23 | break; |
|
3514 | 28 | case '-': // Subtraction |
|
3515 | 7 | $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack); |
|
3516 | |||
3517 | 7 | break; |
|
3518 | 26 | case '*': // Multiplication |
|
3519 | 19 | $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack); |
|
3520 | |||
3521 | 19 | break; |
|
3522 | 11 | case '/': // Division |
|
3523 | 8 | $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack); |
|
3524 | |||
3525 | 8 | break; |
|
3526 | 6 | case '^': // Exponential |
|
3527 | $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack); |
||
3528 | |||
3529 | break; |
||
3530 | 6 | case '&': // Concatenation |
|
3531 | // If either of the operands is a matrix, we need to treat them both as matrices |
||
3532 | // (converting the other operand to a matrix if need be); then perform the required |
||
3533 | // matrix operation |
||
3534 | 6 | if (is_bool($operand1)) { |
|
3535 | $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; |
||
3536 | } |
||
3537 | 6 | if (is_bool($operand2)) { |
|
3538 | $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; |
||
3539 | } |
||
3540 | 6 | if ((is_array($operand1)) || (is_array($operand2))) { |
|
3541 | // Ensure that both operands are arrays/matrices |
||
3542 | 5 | self::checkMatrixOperands($operand1, $operand2, 2); |
|
3543 | |||
3544 | try { |
||
3545 | // Convert operand 1 from a PHP array to a matrix |
||
3546 | 5 | $matrix = new Shared\JAMA\Matrix($operand1); |
|
3547 | // Perform the required operation against the operand 1 matrix, passing in operand 2 |
||
3548 | 5 | $matrixResult = $matrix->concat($operand2); |
|
3549 | 5 | $result = $matrixResult->getArray(); |
|
3550 | } catch (\Exception $ex) { |
||
3551 | $this->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); |
||
3552 | 5 | $result = '#VALUE!'; |
|
3553 | } |
||
3554 | } else { |
||
3555 | 1 | $result = '"' . str_replace('""', '"', self::unwrapResult($operand1) . self::unwrapResult($operand2)) . '"'; |
|
3556 | } |
||
3557 | 6 | $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); |
|
3558 | 6 | $stack->push('Value', $result); |
|
3559 | |||
3560 | 6 | break; |
|
3561 | case '|': // Intersect |
||
3562 | $rowIntersect = array_intersect_key($operand1, $operand2); |
||
3563 | $cellIntersect = $oCol = $oRow = []; |
||
3564 | foreach (array_keys($rowIntersect) as $row) { |
||
3565 | $oRow[] = $row; |
||
3566 | foreach ($rowIntersect[$row] as $col => $data) { |
||
3567 | $oCol[] = Cell::columnIndexFromString($col) - 1; |
||
3568 | $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]); |
||
3569 | } |
||
3570 | } |
||
3571 | $cellRef = Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . Cell::stringFromColumnIndex(max($oCol)) . max($oRow); |
||
3572 | $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect)); |
||
3573 | $stack->push('Value', $cellIntersect, $cellRef); |
||
3574 | |||
3575 | 111 | break; |
|
3576 | } |
||
3577 | |||
3578 | // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on |
||
3579 | 118 | } elseif (($token === '~') || ($token === '%')) { |
|
3580 | 2 | if (($arg = $stack->pop()) === null) { |
|
3581 | return $this->raiseFormulaError('Internal error - Operand value missing from stack'); |
||
3582 | } |
||
3583 | 2 | $arg = $arg['value']; |
|
3584 | 2 | if ($token === '~') { |
|
3585 | 2 | $this->debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg)); |
|
3586 | 2 | $multiplier = -1; |
|
3587 | } else { |
||
3588 | $this->debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg)); |
||
3589 | $multiplier = 0.01; |
||
3590 | } |
||
3591 | 2 | if (is_array($arg)) { |
|
3592 | self::checkMatrixOperands($arg, $multiplier, 2); |
||
3593 | |||
3594 | try { |
||
3595 | $matrix1 = new Shared\JAMA\Matrix($arg); |
||
3596 | $matrixResult = $matrix1->arrayTimesEquals($multiplier); |
||
3597 | $result = $matrixResult->getArray(); |
||
3598 | } catch (\Exception $ex) { |
||
3599 | $this->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); |
||
3600 | $result = '#VALUE!'; |
||
3601 | } |
||
3602 | $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); |
||
3603 | $stack->push('Value', $result); |
||
3604 | } else { |
||
3605 | 2 | $this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack); |
|
3606 | } |
||
3607 | 118 | } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) { |
|
3608 | 60 | $cellRef = null; |
|
3609 | 60 | if (isset($matches[8])) { |
|
3610 | if ($pCell === null) { |
||
3611 | // We can't access the range, so return a REF error |
||
3612 | $cellValue = Functions::REF(); |
||
3613 | } else { |
||
3614 | $cellRef = $matches[6] . $matches[7] . ':' . $matches[9] . $matches[10]; |
||
3615 | if ($matches[2] > '') { |
||
3616 | $matches[2] = trim($matches[2], "\"'"); |
||
3617 | View Code Duplication | if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) { |
|
3618 | // It's a Reference to an external spreadsheet (not currently supported) |
||
3619 | return $this->raiseFormulaError('Unable to access External Workbook'); |
||
3620 | } |
||
3621 | $matches[2] = trim($matches[2], "\"'"); |
||
3622 | $this->debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]); |
||
3623 | View Code Duplication | if ($pCellParent !== null) { |
|
3624 | $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false); |
||
3625 | } else { |
||
3626 | return $this->raiseFormulaError('Unable to access Cell Reference'); |
||
3627 | } |
||
3628 | $this->debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue)); |
||
3629 | } else { |
||
3630 | $this->debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet'); |
||
3631 | if ($pCellParent !== null) { |
||
3632 | $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); |
||
3633 | } else { |
||
3634 | return $this->raiseFormulaError('Unable to access Cell Reference'); |
||
3635 | } |
||
3636 | $this->debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue)); |
||
3637 | } |
||
3638 | } |
||
3639 | } else { |
||
3640 | 60 | if ($pCell === null) { |
|
3641 | // We can't access the cell, so return a REF error |
||
3642 | $cellValue = Functions::REF(); |
||
3643 | } else { |
||
3644 | 60 | $cellRef = $matches[6] . $matches[7]; |
|
3645 | 60 | if ($matches[2] > '') { |
|
3646 | 14 | $matches[2] = trim($matches[2], "\"'"); |
|
3647 | 14 | View Code Duplication | if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) { |
3648 | // It's a Reference to an external spreadsheet (not currently supported) |
||
3649 | return $this->raiseFormulaError('Unable to access External Workbook'); |
||
3650 | } |
||
3651 | 14 | $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]); |
|
3652 | 14 | if ($pCellParent !== null) { |
|
3653 | 14 | $cellSheet = $this->spreadsheet->getSheetByName($matches[2]); |
|
3654 | 14 | if ($cellSheet && $cellSheet->cellExists($cellRef)) { |
|
3655 | 14 | $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false); |
|
3656 | 14 | $pCell->attach($pCellParent); |
|
3657 | } else { |
||
3658 | 14 | $cellValue = null; |
|
3659 | } |
||
3660 | } else { |
||
3661 | return $this->raiseFormulaError('Unable to access Cell Reference'); |
||
3662 | } |
||
3663 | 14 | $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue)); |
|
3664 | } else { |
||
3665 | 48 | $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet'); |
|
3666 | 48 | if ($pCellParent->has($cellRef)) { |
|
3667 | 48 | $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); |
|
3668 | 48 | $pCell->attach($pCellParent); |
|
3669 | } else { |
||
3670 | 2 | $cellValue = null; |
|
3671 | } |
||
3672 | 48 | $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue)); |
|
3673 | } |
||
3674 | } |
||
3675 | } |
||
3676 | 60 | $stack->push('Value', $cellValue, $cellRef); |
|
3677 | |||
3678 | // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on |
||
3679 | 104 | } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $token, $matches)) { |
|
3680 | 46 | $functionName = $matches[1]; |
|
3681 | 46 | $argCount = $stack->pop(); |
|
3682 | 46 | $argCount = $argCount['value']; |
|
3683 | 46 | if ($functionName != 'MKMATRIX') { |
|
3684 | 46 | $this->debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's')); |
|
3685 | } |
||
3686 | 46 | if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function |
|
3687 | 46 | if (isset(self::$phpSpreadsheetFunctions[$functionName])) { |
|
3688 | 46 | $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall']; |
|
3689 | 46 | $passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']); |
|
3690 | 46 | $passCellReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passCellReference']); |
|
3691 | } elseif (isset(self::$controlFunctions[$functionName])) { |
||
3692 | $functionCall = self::$controlFunctions[$functionName]['functionCall']; |
||
3693 | $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']); |
||
3694 | $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']); |
||
3695 | } |
||
3696 | // get the arguments for this function |
||
3697 | 46 | $args = $argArrayVals = []; |
|
3698 | 46 | for ($i = 0; $i < $argCount; ++$i) { |
|
3699 | 46 | $arg = $stack->pop(); |
|
3700 | 46 | $a = $argCount - $i - 1; |
|
3701 | 46 | if (($passByReference) && |
|
3702 | 46 | (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) && |
|
3703 | 46 | (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) { |
|
3704 | if ($arg['reference'] === null) { |
||
3705 | $args[] = $cellID; |
||
3706 | if ($functionName != 'MKMATRIX') { |
||
3707 | $argArrayVals[] = $this->showValue($cellID); |
||
3708 | } |
||
3709 | } else { |
||
3710 | $args[] = $arg['reference']; |
||
3711 | if ($functionName != 'MKMATRIX') { |
||
3712 | $argArrayVals[] = $this->showValue($arg['reference']); |
||
3713 | } |
||
3714 | } |
||
3715 | } else { |
||
3716 | 46 | $args[] = self::unwrapResult($arg['value']); |
|
3717 | 46 | if ($functionName != 'MKMATRIX') { |
|
3718 | 46 | $argArrayVals[] = $this->showValue($arg['value']); |
|
3719 | } |
||
3720 | } |
||
3721 | } |
||
3722 | // Reverse the order of the arguments |
||
3723 | 46 | krsort($args); |
|
3724 | |||
3725 | 46 | if (($passByReference) && ($argCount == 0)) { |
|
3726 | $args[] = $cellID; |
||
3727 | $argArrayVals[] = $this->showValue($cellID); |
||
3728 | } |
||
3729 | |||
3730 | 46 | if ($functionName != 'MKMATRIX') { |
|
3731 | 46 | if ($this->debugLog->getWriteDebugLog()) { |
|
3732 | krsort($argArrayVals); |
||
3733 | $this->debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator . ' ', Functions::flattenArray($argArrayVals)), ' )'); |
||
3734 | } |
||
3735 | } |
||
3736 | |||
3737 | // Process the argument with the appropriate function call |
||
3738 | 46 | if ($passCellReference) { |
|
3739 | 1 | $args[] = $pCell; |
|
3740 | } |
||
3741 | |||
3742 | 46 | if (!is_array($functionCall)) { |
|
3743 | 1 | foreach ($args as &$arg) { |
|
3744 | $arg = Functions::flattenSingleValue($arg); |
||
3745 | } |
||
3746 | 1 | unset($arg); |
|
3747 | } |
||
3748 | 46 | $result = call_user_func_array($functionCall, $args); |
|
3749 | |||
3750 | 46 | if ($functionName != 'MKMATRIX') { |
|
3751 | 46 | $this->debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result)); |
|
3752 | } |
||
3753 | 46 | $stack->push('Value', self::wrapResult($result)); |
|
3754 | } |
||
3755 | } else { |
||
3756 | // if the token is a number, boolean, string or an Excel error, push it onto the stack |
||
3757 | 104 | if (isset(self::$excelConstants[strtoupper($token)])) { |
|
3758 | $excelConstant = strtoupper($token); |
||
3759 | $stack->push('Constant Value', self::$excelConstants[$excelConstant]); |
||
3760 | $this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant])); |
||
3761 | 104 | } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == '"') || ($token[0] == '#')) { |
|
3762 | 104 | $stack->push('Value', $token); |
|
3763 | // if the token is a named range, push the named range name onto the stack |
||
3764 | 5 | } elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) { |
|
3765 | 5 | $namedRange = $matches[6]; |
|
3766 | 5 | $this->debugLog->writeDebugLog('Evaluating Named Range ', $namedRange); |
|
3767 | |||
3768 | 5 | if (substr($namedRange, 0, 6) === '_xlfn.') { |
|
3769 | return $this->raiseFormulaError("undefined named range / function '$token'"); |
||
3770 | } |
||
3771 | |||
3772 | 5 | $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false); |
|
3773 | 5 | $pCell->attach($pCellParent); |
|
3774 | 5 | $this->debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue)); |
|
3775 | 5 | $stack->push('Named Range', $cellValue, $namedRange); |
|
3776 | } else { |
||
3777 | 118 | return $this->raiseFormulaError("undefined variable '$token'"); |
|
3778 | } |
||
3779 | } |
||
3780 | } |
||
3781 | // when we're out of tokens, the stack should have a single element, the final result |
||
3782 | 118 | if ($stack->count() != 1) { |
|
3783 | return $this->raiseFormulaError('internal error'); |
||
3784 | } |
||
3785 | 118 | $output = $stack->pop(); |
|
3786 | 118 | $output = $output['value']; |
|
3787 | |||
3788 | 118 | return $output; |
|
3789 | } |
||
3790 | |||
3791 | 31 | private function validateBinaryOperand(&$operand, &$stack) |
|
3828 | |||
3829 | /** |
||
3830 | * @param null|string $cellID |
||
3831 | * @param mixed $operand1 |
||
3832 | * @param mixed $operand2 |
||
3833 | * @param string $operation |
||
3834 | * @param Stack $stack |
||
3835 | * @param bool $recursingArrays |
||
3836 | * |
||
3837 | * @return bool |
||
3838 | */ |
||
3839 | 74 | private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false) |
|
3966 | |||
3967 | /** |
||
3968 | * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters. |
||
3969 | * |
||
3970 | * @param string $str1 First string value for the comparison |
||
3971 | * @param string $str2 Second string value for the comparison |
||
3972 | * |
||
3973 | * @return int |
||
3974 | */ |
||
3975 | 21 | private function strcmpLowercaseFirst($str1, $str2) |
|
3982 | |||
3983 | /** |
||
3984 | * @param mixed $operand1 |
||
3985 | * @param mixed $operand2 |
||
3986 | * @param mixed $operation |
||
3987 | * @param string $matrixFunction |
||
3988 | * @param mixed $stack |
||
3989 | * |
||
3990 | * @return bool |
||
3991 | */ |
||
3992 | 31 | private function executeNumericBinaryOperation($operand1, $operand2, $operation, $matrixFunction, &$stack) |
|
4070 | |||
4071 | // trigger an error, but nicely, if need be |
||
4072 | protected function raiseFormulaError($errorMessage) |
||
4081 | |||
4082 | /** |
||
4083 | * Extract range values. |
||
4084 | * |
||
4085 | * @param string &$pRange String based range representation |
||
4086 | * @param Worksheet $pSheet Worksheet |
||
4087 | * @param bool $resetLog Flag indicating whether calculation log should be reset or not |
||
4088 | * |
||
4089 | * @throws Exception |
||
4090 | * |
||
4091 | * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. |
||
4092 | */ |
||
4093 | 60 | public function extractCellRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true) |
|
4132 | |||
4133 | /** |
||
4134 | * Extract range values. |
||
4135 | * |
||
4136 | * @param string &$pRange String based range representation |
||
4137 | * @param Worksheet $pSheet Worksheet |
||
4138 | * @param bool $resetLog Flag indicating whether calculation log should be reset or not |
||
4139 | * |
||
4140 | * @throws Exception |
||
4141 | * |
||
4142 | * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. |
||
4143 | */ |
||
4144 | 5 | public function extractNamedRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true) |
|
4198 | |||
4199 | /** |
||
4200 | * Is a specific function implemented? |
||
4201 | * |
||
4202 | * @param string $pFunction Function Name |
||
4203 | * |
||
4204 | * @return bool |
||
4205 | */ |
||
4206 | 3 | public function isImplemented($pFunction) |
|
4213 | |||
4214 | /** |
||
4215 | * Get a list of all implemented functions as an array of function objects. |
||
4216 | * |
||
4217 | * @return array of Category |
||
4218 | */ |
||
4219 | public function getFunctions() |
||
4223 | |||
4224 | /** |
||
4225 | * Get a list of implemented Excel function names. |
||
4226 | * |
||
4227 | * @return array |
||
4228 | */ |
||
4229 | 2 | public function getImplementedFunctionNames() |
|
4240 | } |
||
4241 |
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.