1<?php 2 3/** PHPExcel root directory */ 4if (!defined('PHPEXCEL_ROOT')) { 5 /** 6 * @ignore 7 */ 8 define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../'); 9 require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php'); 10} 11 12if (!defined('CALCULATION_REGEXP_CELLREF')) { 13 // Test for support of \P (multibyte options) in PCRE 14 if (defined('PREG_BAD_UTF8_ERROR')) { 15 // Cell reference (cell or range of cells, with or without a sheet reference) 16 define('CALCULATION_REGEXP_CELLREF', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})'); 17 // Named Range of cells 18 define('CALCULATION_REGEXP_NAMEDRANGE', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)'); 19 } else { 20 // Cell reference (cell or range of cells, with or without a sheet reference) 21 define('CALCULATION_REGEXP_CELLREF', '(((\w*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d+)'); 22 // Named Range of cells 23 define('CALCULATION_REGEXP_NAMEDRANGE', '(((\w*)|(\'.*\')|(\".*\"))!)?([_A-Z][_A-Z0-9\.]*)'); 24 } 25} 26 27/** 28 * PHPExcel_Calculation (Multiton) 29 * 30 * Copyright (c) 2006 - 2015 PHPExcel 31 * 32 * This library is free software; you can redistribute it and/or 33 * modify it under the terms of the GNU Lesser General Public 34 * License as published by the Free Software Foundation; either 35 * version 2.1 of the License, or (at your option) any later version. 36 * 37 * This library is distributed in the hope that it will be useful, 38 * but WITHOUT ANY WARRANTY; without even the implied warranty of 39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 40 * Lesser General Public License for more details. 41 * 42 * You should have received a copy of the GNU Lesser General Public 43 * License along with this library; if not, write to the Free Software 44 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 45 * 46 * @category PHPExcel 47 * @package PHPExcel_Calculation 48 * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel) 49 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL 50 * @version ##VERSION##, ##DATE## 51 */ 52class PHPExcel_Calculation 53{ 54 /** Constants */ 55 /** Regular Expressions */ 56 // Numeric operand 57 const CALCULATION_REGEXP_NUMBER = '[-+]?\d*\.?\d+(e[-+]?\d+)?'; 58 // String operand 59 const CALCULATION_REGEXP_STRING = '"(?:[^"]|"")*"'; 60 // Opening bracket 61 const CALCULATION_REGEXP_OPENBRACE = '\('; 62 // Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it) 63 const CALCULATION_REGEXP_FUNCTION = '@?([A-Z][A-Z0-9\.]*)[\s]*\('; 64 // Cell reference (cell or range of cells, with or without a sheet reference) 65 const CALCULATION_REGEXP_CELLREF = CALCULATION_REGEXP_CELLREF; 66 // Named Range of cells 67 const CALCULATION_REGEXP_NAMEDRANGE = CALCULATION_REGEXP_NAMEDRANGE; 68 // Error 69 const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?'; 70 71 72 /** constants */ 73 const RETURN_ARRAY_AS_ERROR = 'error'; 74 const RETURN_ARRAY_AS_VALUE = 'value'; 75 const RETURN_ARRAY_AS_ARRAY = 'array'; 76 77 private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE; 78 79 80 /** 81 * Instance of this class 82 * 83 * @access private 84 * @var PHPExcel_Calculation 85 */ 86 private static $instance; 87 88 89 /** 90 * Instance of the workbook this Calculation Engine is using 91 * 92 * @access private 93 * @var PHPExcel 94 */ 95 private $workbook; 96 97 /** 98 * List of instances of the calculation engine that we've instantiated for individual workbooks 99 * 100 * @access private 101 * @var PHPExcel_Calculation[] 102 */ 103 private static $workbookSets; 104 105 /** 106 * Calculation cache 107 * 108 * @access private 109 * @var array 110 */ 111 private $calculationCache = array (); 112 113 114 /** 115 * Calculation cache enabled 116 * 117 * @access private 118 * @var boolean 119 */ 120 private $calculationCacheEnabled = true; 121 122 123 /** 124 * List of operators that can be used within formulae 125 * The true/false value indicates whether it is a binary operator or a unary operator 126 * 127 * @access private 128 * @var array 129 */ 130 private static $operators = array( 131 '+' => true, '-' => true, '*' => true, '/' => true, 132 '^' => true, '&' => true, '%' => false, '~' => false, 133 '>' => true, '<' => true, '=' => true, '>=' => true, 134 '<=' => true, '<>' => true, '|' => true, ':' => true 135 ); 136 137 /** 138 * List of binary operators (those that expect two operands) 139 * 140 * @access private 141 * @var array 142 */ 143 private static $binaryOperators = array( 144 '+' => true, '-' => true, '*' => true, '/' => true, 145 '^' => true, '&' => true, '>' => true, '<' => true, 146 '=' => true, '>=' => true, '<=' => true, '<>' => true, 147 '|' => true, ':' => true 148 ); 149 150 /** 151 * The debug log generated by the calculation engine 152 * 153 * @access private 154 * @var PHPExcel_CalcEngine_Logger 155 * 156 */ 157 private $debugLog; 158 159 /** 160 * Flag to determine how formula errors should be handled 161 * If true, then a user error will be triggered 162 * If false, then an exception will be thrown 163 * 164 * @access public 165 * @var boolean 166 * 167 */ 168 public $suppressFormulaErrors = false; 169 170 /** 171 * Error message for any error that was raised/thrown by the calculation engine 172 * 173 * @access public 174 * @var string 175 * 176 */ 177 public $formulaError = null; 178 179 /** 180 * An array of the nested cell references accessed by the calculation engine, used for the debug log 181 * 182 * @access private 183 * @var array of string 184 * 185 */ 186 private $cyclicReferenceStack; 187 188 private $cellStack = array(); 189 190 /** 191 * Current iteration counter for cyclic formulae 192 * If the value is 0 (or less) then cyclic formulae will throw an exception, 193 * otherwise they will iterate to the limit defined here before returning a result 194 * 195 * @var integer 196 * 197 */ 198 private $cyclicFormulaCounter = 1; 199 200 private $cyclicFormulaCell = ''; 201 202 /** 203 * Number of iterations for cyclic formulae 204 * 205 * @var integer 206 * 207 */ 208 public $cyclicFormulaCount = 1; 209 210 /** 211 * Epsilon Precision used for comparisons in calculations 212 * 213 * @var float 214 * 215 */ 216 private $delta = 0.1e-12; 217 218 219 /** 220 * The current locale setting 221 * 222 * @var string 223 * 224 */ 225 private static $localeLanguage = 'en_us'; // US English (default locale) 226 227 /** 228 * List of available locale settings 229 * Note that this is read for the locale subdirectory only when requested 230 * 231 * @var string[] 232 * 233 */ 234 private static $validLocaleLanguages = array( 235 'en' // English (default language) 236 ); 237 238 /** 239 * Locale-specific argument separator for function arguments 240 * 241 * @var string 242 * 243 */ 244 private static $localeArgumentSeparator = ','; 245 private static $localeFunctions = array(); 246 247 /** 248 * Locale-specific translations for Excel constants (True, False and Null) 249 * 250 * @var string[] 251 * 252 */ 253 public static $localeBoolean = array( 254 'TRUE' => 'TRUE', 255 'FALSE' => 'FALSE', 256 'NULL' => 'NULL' 257 ); 258 259 /** 260 * Excel constant string translations to their PHP equivalents 261 * Constant conversion from text name/value to actual (datatyped) value 262 * 263 * @var string[] 264 * 265 */ 266 private static $excelConstants = array( 267 'TRUE' => true, 268 'FALSE' => false, 269 'NULL' => null 270 ); 271 272 // PHPExcel functions 273 private static $PHPExcelFunctions = array( 274 'ABS' => array( 275 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 276 'functionCall' => 'abs', 277 'argumentCount' => '1' 278 ), 279 'ACCRINT' => array( 280 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 281 'functionCall' => 'PHPExcel_Calculation_Financial::ACCRINT', 282 'argumentCount' => '4-7' 283 ), 284 'ACCRINTM' => array( 285 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 286 'functionCall' => 'PHPExcel_Calculation_Financial::ACCRINTM', 287 'argumentCount' => '3-5' 288 ), 289 'ACOS' => array( 290 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 291 'functionCall' => 'acos', 292 'argumentCount' => '1' 293 ), 294 'ACOSH' => array( 295 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 296 'functionCall' => 'acosh', 297 'argumentCount' => '1' 298 ), 299 'ADDRESS' => array( 300 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 301 'functionCall' => 'PHPExcel_Calculation_LookupRef::CELL_ADDRESS', 302 'argumentCount' => '2-5' 303 ), 304 'AMORDEGRC' => array( 305 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 306 'functionCall' => 'PHPExcel_Calculation_Financial::AMORDEGRC', 307 'argumentCount' => '6,7' 308 ), 309 'AMORLINC' => array( 310 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 311 'functionCall' => 'PHPExcel_Calculation_Financial::AMORLINC', 312 'argumentCount' => '6,7' 313 ), 314 'AND' => array( 315 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 316 'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_AND', 317 'argumentCount' => '1+' 318 ), 319 'AREAS' => array( 320 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 321 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 322 'argumentCount' => '1' 323 ), 324 'ASC' => array( 325 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 326 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 327 'argumentCount' => '1' 328 ), 329 'ASIN' => array( 330 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 331 'functionCall' => 'asin', 332 'argumentCount' => '1' 333 ), 334 'ASINH' => array( 335 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 336 'functionCall' => 'asinh', 337 'argumentCount' => '1' 338 ), 339 'ATAN' => array( 340 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 341 'functionCall' => 'atan', 342 'argumentCount' => '1' 343 ), 344 'ATAN2' => array( 345 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 346 'functionCall' => 'PHPExcel_Calculation_MathTrig::ATAN2', 347 'argumentCount' => '2' 348 ), 349 'ATANH' => array( 350 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 351 'functionCall' => 'atanh', 352 'argumentCount' => '1' 353 ), 354 'AVEDEV' => array( 355 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 356 'functionCall' => 'PHPExcel_Calculation_Statistical::AVEDEV', 357 'argumentCount' => '1+' 358 ), 359 'AVERAGE' => array( 360 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 361 'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGE', 362 'argumentCount' => '1+' 363 ), 364 'AVERAGEA' => array( 365 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 366 'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGEA', 367 'argumentCount' => '1+' 368 ), 369 'AVERAGEIF' => array( 370 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 371 'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGEIF', 372 'argumentCount' => '2,3' 373 ), 374 'AVERAGEIFS' => array( 375 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 376 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 377 'argumentCount' => '3+' 378 ), 379 'BAHTTEXT' => array( 380 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 381 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 382 'argumentCount' => '1' 383 ), 384 'BESSELI' => array( 385 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 386 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELI', 387 'argumentCount' => '2' 388 ), 389 'BESSELJ' => array( 390 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 391 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELJ', 392 'argumentCount' => '2' 393 ), 394 'BESSELK' => array( 395 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 396 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELK', 397 'argumentCount' => '2' 398 ), 399 'BESSELY' => array( 400 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 401 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELY', 402 'argumentCount' => '2' 403 ), 404 'BETADIST' => array( 405 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 406 'functionCall' => 'PHPExcel_Calculation_Statistical::BETADIST', 407 'argumentCount' => '3-5' 408 ), 409 'BETAINV' => array( 410 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 411 'functionCall' => 'PHPExcel_Calculation_Statistical::BETAINV', 412 'argumentCount' => '3-5' 413 ), 414 'BIN2DEC' => array( 415 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 416 'functionCall' => 'PHPExcel_Calculation_Engineering::BINTODEC', 417 'argumentCount' => '1' 418 ), 419 'BIN2HEX' => array( 420 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 421 'functionCall' => 'PHPExcel_Calculation_Engineering::BINTOHEX', 422 'argumentCount' => '1,2' 423 ), 424 'BIN2OCT' => array( 425 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 426 'functionCall' => 'PHPExcel_Calculation_Engineering::BINTOOCT', 427 'argumentCount' => '1,2' 428 ), 429 'BINOMDIST' => array( 430 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 431 'functionCall' => 'PHPExcel_Calculation_Statistical::BINOMDIST', 432 'argumentCount' => '4' 433 ), 434 'CEILING' => array( 435 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 436 'functionCall' => 'PHPExcel_Calculation_MathTrig::CEILING', 437 'argumentCount' => '2' 438 ), 439 'CELL' => array( 440 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 441 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 442 'argumentCount' => '1,2' 443 ), 444 'CHAR' => array( 445 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 446 'functionCall' => 'PHPExcel_Calculation_TextData::CHARACTER', 447 'argumentCount' => '1' 448 ), 449 'CHIDIST' => array( 450 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 451 'functionCall' => 'PHPExcel_Calculation_Statistical::CHIDIST', 452 'argumentCount' => '2' 453 ), 454 'CHIINV' => array( 455 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 456 'functionCall' => 'PHPExcel_Calculation_Statistical::CHIINV', 457 'argumentCount' => '2' 458 ), 459 'CHITEST' => array( 460 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 461 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 462 'argumentCount' => '2' 463 ), 464 'CHOOSE' => array( 465 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 466 'functionCall' => 'PHPExcel_Calculation_LookupRef::CHOOSE', 467 'argumentCount' => '2+' 468 ), 469 'CLEAN' => array( 470 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 471 'functionCall' => 'PHPExcel_Calculation_TextData::TRIMNONPRINTABLE', 472 'argumentCount' => '1' 473 ), 474 'CODE' => array( 475 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 476 'functionCall' => 'PHPExcel_Calculation_TextData::ASCIICODE', 477 'argumentCount' => '1' 478 ), 479 'COLUMN' => array( 480 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 481 'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMN', 482 'argumentCount' => '-1', 483 'passByReference' => array(true) 484 ), 485 'COLUMNS' => array( 486 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 487 'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMNS', 488 'argumentCount' => '1' 489 ), 490 'COMBIN' => array( 491 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 492 'functionCall' => 'PHPExcel_Calculation_MathTrig::COMBIN', 493 'argumentCount' => '2' 494 ), 495 'COMPLEX' => array( 496 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 497 'functionCall' => 'PHPExcel_Calculation_Engineering::COMPLEX', 498 'argumentCount' => '2,3' 499 ), 500 'CONCATENATE' => array( 501 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 502 'functionCall' => 'PHPExcel_Calculation_TextData::CONCATENATE', 503 'argumentCount' => '1+' 504 ), 505 'CONFIDENCE' => array( 506 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 507 'functionCall' => 'PHPExcel_Calculation_Statistical::CONFIDENCE', 508 'argumentCount' => '3' 509 ), 510 'CONVERT' => array( 511 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 512 'functionCall' => 'PHPExcel_Calculation_Engineering::CONVERTUOM', 513 'argumentCount' => '3' 514 ), 515 'CORREL' => array( 516 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 517 'functionCall' => 'PHPExcel_Calculation_Statistical::CORREL', 518 'argumentCount' => '2' 519 ), 520 'COS' => array( 521 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 522 'functionCall' => 'cos', 523 'argumentCount' => '1' 524 ), 525 'COSH' => array( 526 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 527 'functionCall' => 'cosh', 528 'argumentCount' => '1' 529 ), 530 'COUNT' => array( 531 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 532 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNT', 533 'argumentCount' => '1+' 534 ), 535 'COUNTA' => array( 536 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 537 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTA', 538 'argumentCount' => '1+' 539 ), 540 'COUNTBLANK' => array( 541 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 542 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTBLANK', 543 'argumentCount' => '1' 544 ), 545 'COUNTIF' => array( 546 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 547 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTIF', 548 'argumentCount' => '2' 549 ), 550 'COUNTIFS' => array( 551 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 552 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 553 'argumentCount' => '2' 554 ), 555 'COUPDAYBS' => array( 556 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 557 'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYBS', 558 'argumentCount' => '3,4' 559 ), 560 'COUPDAYS' => array( 561 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 562 'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYS', 563 'argumentCount' => '3,4' 564 ), 565 'COUPDAYSNC' => array( 566 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 567 'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYSNC', 568 'argumentCount' => '3,4' 569 ), 570 'COUPNCD' => array( 571 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 572 'functionCall' => 'PHPExcel_Calculation_Financial::COUPNCD', 573 'argumentCount' => '3,4' 574 ), 575 'COUPNUM' => array( 576 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 577 'functionCall' => 'PHPExcel_Calculation_Financial::COUPNUM', 578 'argumentCount' => '3,4' 579 ), 580 'COUPPCD' => array( 581 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 582 'functionCall' => 'PHPExcel_Calculation_Financial::COUPPCD', 583 'argumentCount' => '3,4' 584 ), 585 'COVAR' => array( 586 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 587 'functionCall' => 'PHPExcel_Calculation_Statistical::COVAR', 588 'argumentCount' => '2' 589 ), 590 'CRITBINOM' => array( 591 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 592 'functionCall' => 'PHPExcel_Calculation_Statistical::CRITBINOM', 593 'argumentCount' => '3' 594 ), 595 'CUBEKPIMEMBER' => array( 596 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 597 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 598 'argumentCount' => '?' 599 ), 600 'CUBEMEMBER' => array( 601 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 602 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 603 'argumentCount' => '?' 604 ), 605 'CUBEMEMBERPROPERTY' => array( 606 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 607 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 608 'argumentCount' => '?' 609 ), 610 'CUBERANKEDMEMBER' => array( 611 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 612 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 613 'argumentCount' => '?' 614 ), 615 'CUBESET' => array( 616 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 617 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 618 'argumentCount' => '?' 619 ), 620 'CUBESETCOUNT' => array( 621 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 622 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 623 'argumentCount' => '?' 624 ), 625 'CUBEVALUE' => array( 626 'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE, 627 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 628 'argumentCount' => '?' 629 ), 630 'CUMIPMT' => array( 631 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 632 'functionCall' => 'PHPExcel_Calculation_Financial::CUMIPMT', 633 'argumentCount' => '6' 634 ), 635 'CUMPRINC' => array( 636 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 637 'functionCall' => 'PHPExcel_Calculation_Financial::CUMPRINC', 638 'argumentCount' => '6' 639 ), 640 'DATE' => array( 641 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 642 'functionCall' => 'PHPExcel_Calculation_DateTime::DATE', 643 'argumentCount' => '3' 644 ), 645 'DATEDIF' => array( 646 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 647 'functionCall' => 'PHPExcel_Calculation_DateTime::DATEDIF', 648 'argumentCount' => '2,3' 649 ), 650 'DATEVALUE' => array( 651 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 652 'functionCall' => 'PHPExcel_Calculation_DateTime::DATEVALUE', 653 'argumentCount' => '1' 654 ), 655 'DAVERAGE' => array( 656 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 657 'functionCall' => 'PHPExcel_Calculation_Database::DAVERAGE', 658 'argumentCount' => '3' 659 ), 660 'DAY' => array( 661 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 662 'functionCall' => 'PHPExcel_Calculation_DateTime::DAYOFMONTH', 663 'argumentCount' => '1' 664 ), 665 'DAYS360' => array( 666 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 667 'functionCall' => 'PHPExcel_Calculation_DateTime::DAYS360', 668 'argumentCount' => '2,3' 669 ), 670 'DB' => array( 671 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 672 'functionCall' => 'PHPExcel_Calculation_Financial::DB', 673 'argumentCount' => '4,5' 674 ), 675 'DCOUNT' => array( 676 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 677 'functionCall' => 'PHPExcel_Calculation_Database::DCOUNT', 678 'argumentCount' => '3' 679 ), 680 'DCOUNTA' => array( 681 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 682 'functionCall' => 'PHPExcel_Calculation_Database::DCOUNTA', 683 'argumentCount' => '3' 684 ), 685 'DDB' => array( 686 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 687 'functionCall' => 'PHPExcel_Calculation_Financial::DDB', 688 'argumentCount' => '4,5' 689 ), 690 'DEC2BIN' => array( 691 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 692 'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOBIN', 693 'argumentCount' => '1,2' 694 ), 695 'DEC2HEX' => array( 696 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 697 'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOHEX', 698 'argumentCount' => '1,2' 699 ), 700 'DEC2OCT' => array( 701 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 702 'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOOCT', 703 'argumentCount' => '1,2' 704 ), 705 'DEGREES' => array( 706 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 707 'functionCall' => 'rad2deg', 708 'argumentCount' => '1' 709 ), 710 'DELTA' => array( 711 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 712 'functionCall' => 'PHPExcel_Calculation_Engineering::DELTA', 713 'argumentCount' => '1,2' 714 ), 715 'DEVSQ' => array( 716 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 717 'functionCall' => 'PHPExcel_Calculation_Statistical::DEVSQ', 718 'argumentCount' => '1+' 719 ), 720 'DGET' => array( 721 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 722 'functionCall' => 'PHPExcel_Calculation_Database::DGET', 723 'argumentCount' => '3' 724 ), 725 'DISC' => array( 726 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 727 'functionCall' => 'PHPExcel_Calculation_Financial::DISC', 728 'argumentCount' => '4,5' 729 ), 730 'DMAX' => array( 731 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 732 'functionCall' => 'PHPExcel_Calculation_Database::DMAX', 733 'argumentCount' => '3' 734 ), 735 'DMIN' => array( 736 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 737 'functionCall' => 'PHPExcel_Calculation_Database::DMIN', 738 'argumentCount' => '3' 739 ), 740 'DOLLAR' => array( 741 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 742 'functionCall' => 'PHPExcel_Calculation_TextData::DOLLAR', 743 'argumentCount' => '1,2' 744 ), 745 'DOLLARDE' => array( 746 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 747 'functionCall' => 'PHPExcel_Calculation_Financial::DOLLARDE', 748 'argumentCount' => '2' 749 ), 750 'DOLLARFR' => array( 751 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 752 'functionCall' => 'PHPExcel_Calculation_Financial::DOLLARFR', 753 'argumentCount' => '2' 754 ), 755 'DPRODUCT' => array( 756 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 757 'functionCall' => 'PHPExcel_Calculation_Database::DPRODUCT', 758 'argumentCount' => '3' 759 ), 760 'DSTDEV' => array( 761 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 762 'functionCall' => 'PHPExcel_Calculation_Database::DSTDEV', 763 'argumentCount' => '3' 764 ), 765 'DSTDEVP' => array( 766 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 767 'functionCall' => 'PHPExcel_Calculation_Database::DSTDEVP', 768 'argumentCount' => '3' 769 ), 770 'DSUM' => array( 771 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 772 'functionCall' => 'PHPExcel_Calculation_Database::DSUM', 773 'argumentCount' => '3' 774 ), 775 'DURATION' => array( 776 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 777 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 778 'argumentCount' => '5,6' 779 ), 780 'DVAR' => array( 781 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 782 'functionCall' => 'PHPExcel_Calculation_Database::DVAR', 783 'argumentCount' => '3' 784 ), 785 'DVARP' => array( 786 'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE, 787 'functionCall' => 'PHPExcel_Calculation_Database::DVARP', 788 'argumentCount' => '3' 789 ), 790 'EDATE' => array( 791 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 792 'functionCall' => 'PHPExcel_Calculation_DateTime::EDATE', 793 'argumentCount' => '2' 794 ), 795 'EFFECT' => array( 796 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 797 'functionCall' => 'PHPExcel_Calculation_Financial::EFFECT', 798 'argumentCount' => '2' 799 ), 800 'EOMONTH' => array( 801 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 802 'functionCall' => 'PHPExcel_Calculation_DateTime::EOMONTH', 803 'argumentCount' => '2' 804 ), 805 'ERF' => array( 806 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 807 'functionCall' => 'PHPExcel_Calculation_Engineering::ERF', 808 'argumentCount' => '1,2' 809 ), 810 'ERFC' => array( 811 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 812 'functionCall' => 'PHPExcel_Calculation_Engineering::ERFC', 813 'argumentCount' => '1' 814 ), 815 'ERROR.TYPE' => array( 816 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 817 'functionCall' => 'PHPExcel_Calculation_Functions::ERROR_TYPE', 818 'argumentCount' => '1' 819 ), 820 'EVEN' => array( 821 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 822 'functionCall' => 'PHPExcel_Calculation_MathTrig::EVEN', 823 'argumentCount' => '1' 824 ), 825 'EXACT' => array( 826 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 827 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 828 'argumentCount' => '2' 829 ), 830 'EXP' => array( 831 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 832 'functionCall' => 'exp', 833 'argumentCount' => '1' 834 ), 835 'EXPONDIST' => array( 836 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 837 'functionCall' => 'PHPExcel_Calculation_Statistical::EXPONDIST', 838 'argumentCount' => '3' 839 ), 840 'FACT' => array( 841 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 842 'functionCall' => 'PHPExcel_Calculation_MathTrig::FACT', 843 'argumentCount' => '1' 844 ), 845 'FACTDOUBLE' => array( 846 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 847 'functionCall' => 'PHPExcel_Calculation_MathTrig::FACTDOUBLE', 848 'argumentCount' => '1' 849 ), 850 'FALSE' => array( 851 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 852 'functionCall' => 'PHPExcel_Calculation_Logical::FALSE', 853 'argumentCount' => '0' 854 ), 855 'FDIST' => array( 856 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 857 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 858 'argumentCount' => '3' 859 ), 860 'FIND' => array( 861 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 862 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHSENSITIVE', 863 'argumentCount' => '2,3' 864 ), 865 'FINDB' => array( 866 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 867 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHSENSITIVE', 868 'argumentCount' => '2,3' 869 ), 870 'FINV' => array( 871 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 872 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 873 'argumentCount' => '3' 874 ), 875 'FISHER' => array( 876 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 877 'functionCall' => 'PHPExcel_Calculation_Statistical::FISHER', 878 'argumentCount' => '1' 879 ), 880 'FISHERINV' => array( 881 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 882 'functionCall' => 'PHPExcel_Calculation_Statistical::FISHERINV', 883 'argumentCount' => '1' 884 ), 885 'FIXED' => array( 886 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 887 'functionCall' => 'PHPExcel_Calculation_TextData::FIXEDFORMAT', 888 'argumentCount' => '1-3' 889 ), 890 'FLOOR' => array( 891 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 892 'functionCall' => 'PHPExcel_Calculation_MathTrig::FLOOR', 893 'argumentCount' => '2' 894 ), 895 'FORECAST' => array( 896 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 897 'functionCall' => 'PHPExcel_Calculation_Statistical::FORECAST', 898 'argumentCount' => '3' 899 ), 900 'FREQUENCY' => array( 901 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 902 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 903 'argumentCount' => '2' 904 ), 905 'FTEST' => array( 906 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 907 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 908 'argumentCount' => '2' 909 ), 910 'FV' => array( 911 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 912 'functionCall' => 'PHPExcel_Calculation_Financial::FV', 913 'argumentCount' => '3-5' 914 ), 915 'FVSCHEDULE' => array( 916 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 917 'functionCall' => 'PHPExcel_Calculation_Financial::FVSCHEDULE', 918 'argumentCount' => '2' 919 ), 920 'GAMMADIST' => array( 921 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 922 'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMADIST', 923 'argumentCount' => '4' 924 ), 925 'GAMMAINV' => array( 926 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 927 'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMAINV', 928 'argumentCount' => '3' 929 ), 930 'GAMMALN' => array( 931 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 932 'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMALN', 933 'argumentCount' => '1' 934 ), 935 'GCD' => array( 936 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 937 'functionCall' => 'PHPExcel_Calculation_MathTrig::GCD', 938 'argumentCount' => '1+' 939 ), 940 'GEOMEAN' => array( 941 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 942 'functionCall' => 'PHPExcel_Calculation_Statistical::GEOMEAN', 943 'argumentCount' => '1+' 944 ), 945 'GESTEP' => array( 946 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 947 'functionCall' => 'PHPExcel_Calculation_Engineering::GESTEP', 948 'argumentCount' => '1,2' 949 ), 950 'GETPIVOTDATA' => array( 951 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 952 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 953 'argumentCount' => '2+' 954 ), 955 'GROWTH' => array( 956 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 957 'functionCall' => 'PHPExcel_Calculation_Statistical::GROWTH', 958 'argumentCount' => '1-4' 959 ), 960 'HARMEAN' => array( 961 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 962 'functionCall' => 'PHPExcel_Calculation_Statistical::HARMEAN', 963 'argumentCount' => '1+' 964 ), 965 'HEX2BIN' => array( 966 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 967 'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTOBIN', 968 'argumentCount' => '1,2' 969 ), 970 'HEX2DEC' => array( 971 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 972 'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTODEC', 973 'argumentCount' => '1' 974 ), 975 'HEX2OCT' => array( 976 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 977 'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTOOCT', 978 'argumentCount' => '1,2' 979 ), 980 'HLOOKUP' => array( 981 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 982 'functionCall' => 'PHPExcel_Calculation_LookupRef::HLOOKUP', 983 'argumentCount' => '3,4' 984 ), 985 'HOUR' => array( 986 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 987 'functionCall' => 'PHPExcel_Calculation_DateTime::HOUROFDAY', 988 'argumentCount' => '1' 989 ), 990 'HYPERLINK' => array( 991 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 992 'functionCall' => 'PHPExcel_Calculation_LookupRef::HYPERLINK', 993 'argumentCount' => '1,2', 994 'passCellReference' => true 995 ), 996 'HYPGEOMDIST' => array( 997 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 998 'functionCall' => 'PHPExcel_Calculation_Statistical::HYPGEOMDIST', 999 'argumentCount' => '4' 1000 ), 1001 'IF' => array( 1002 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 1003 'functionCall' => 'PHPExcel_Calculation_Logical::STATEMENT_IF', 1004 'argumentCount' => '1-3' 1005 ), 1006 'IFERROR' => array( 1007 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 1008 'functionCall' => 'PHPExcel_Calculation_Logical::IFERROR', 1009 'argumentCount' => '2' 1010 ), 1011 'IMABS' => array( 1012 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1013 'functionCall' => 'PHPExcel_Calculation_Engineering::IMABS', 1014 'argumentCount' => '1' 1015 ), 1016 'IMAGINARY' => array( 1017 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1018 'functionCall' => 'PHPExcel_Calculation_Engineering::IMAGINARY', 1019 'argumentCount' => '1' 1020 ), 1021 'IMARGUMENT' => array( 1022 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1023 'functionCall' => 'PHPExcel_Calculation_Engineering::IMARGUMENT', 1024 'argumentCount' => '1' 1025 ), 1026 'IMCONJUGATE' => array( 1027 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1028 'functionCall' => 'PHPExcel_Calculation_Engineering::IMCONJUGATE', 1029 'argumentCount' => '1' 1030 ), 1031 'IMCOS' => array( 1032 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1033 'functionCall' => 'PHPExcel_Calculation_Engineering::IMCOS', 1034 'argumentCount' => '1' 1035 ), 1036 'IMDIV' => array( 1037 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1038 'functionCall' => 'PHPExcel_Calculation_Engineering::IMDIV', 1039 'argumentCount' => '2' 1040 ), 1041 'IMEXP' => array( 1042 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1043 'functionCall' => 'PHPExcel_Calculation_Engineering::IMEXP', 1044 'argumentCount' => '1' 1045 ), 1046 'IMLN' => array( 1047 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1048 'functionCall' => 'PHPExcel_Calculation_Engineering::IMLN', 1049 'argumentCount' => '1' 1050 ), 1051 'IMLOG10' => array( 1052 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1053 'functionCall' => 'PHPExcel_Calculation_Engineering::IMLOG10', 1054 'argumentCount' => '1' 1055 ), 1056 'IMLOG2' => array( 1057 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1058 'functionCall' => 'PHPExcel_Calculation_Engineering::IMLOG2', 1059 'argumentCount' => '1' 1060 ), 1061 'IMPOWER' => array( 1062 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1063 'functionCall' => 'PHPExcel_Calculation_Engineering::IMPOWER', 1064 'argumentCount' => '2' 1065 ), 1066 'IMPRODUCT' => array( 1067 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1068 'functionCall' => 'PHPExcel_Calculation_Engineering::IMPRODUCT', 1069 'argumentCount' => '1+' 1070 ), 1071 'IMREAL' => array( 1072 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1073 'functionCall' => 'PHPExcel_Calculation_Engineering::IMREAL', 1074 'argumentCount' => '1' 1075 ), 1076 'IMSIN' => array( 1077 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1078 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSIN', 1079 'argumentCount' => '1' 1080 ), 1081 'IMSQRT' => array( 1082 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1083 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSQRT', 1084 'argumentCount' => '1' 1085 ), 1086 'IMSUB' => array( 1087 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1088 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSUB', 1089 'argumentCount' => '2' 1090 ), 1091 'IMSUM' => array( 1092 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1093 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSUM', 1094 'argumentCount' => '1+' 1095 ), 1096 'INDEX' => array( 1097 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1098 'functionCall' => 'PHPExcel_Calculation_LookupRef::INDEX', 1099 'argumentCount' => '1-4' 1100 ), 1101 'INDIRECT' => array( 1102 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1103 'functionCall' => 'PHPExcel_Calculation_LookupRef::INDIRECT', 1104 'argumentCount' => '1,2', 1105 'passCellReference' => true 1106 ), 1107 'INFO' => array( 1108 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1109 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1110 'argumentCount' => '1' 1111 ), 1112 'INT' => array( 1113 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1114 'functionCall' => 'PHPExcel_Calculation_MathTrig::INT', 1115 'argumentCount' => '1' 1116 ), 1117 'INTERCEPT' => array( 1118 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1119 'functionCall' => 'PHPExcel_Calculation_Statistical::INTERCEPT', 1120 'argumentCount' => '2' 1121 ), 1122 'INTRATE' => array( 1123 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1124 'functionCall' => 'PHPExcel_Calculation_Financial::INTRATE', 1125 'argumentCount' => '4,5' 1126 ), 1127 'IPMT' => array( 1128 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1129 'functionCall' => 'PHPExcel_Calculation_Financial::IPMT', 1130 'argumentCount' => '4-6' 1131 ), 1132 'IRR' => array( 1133 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1134 'functionCall' => 'PHPExcel_Calculation_Financial::IRR', 1135 'argumentCount' => '1,2' 1136 ), 1137 'ISBLANK' => array( 1138 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1139 'functionCall' => 'PHPExcel_Calculation_Functions::IS_BLANK', 1140 'argumentCount' => '1' 1141 ), 1142 'ISERR' => array( 1143 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1144 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERR', 1145 'argumentCount' => '1' 1146 ), 1147 'ISERROR' => array( 1148 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1149 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERROR', 1150 'argumentCount' => '1' 1151 ), 1152 'ISEVEN' => array( 1153 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1154 'functionCall' => 'PHPExcel_Calculation_Functions::IS_EVEN', 1155 'argumentCount' => '1' 1156 ), 1157 'ISLOGICAL' => array( 1158 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1159 'functionCall' => 'PHPExcel_Calculation_Functions::IS_LOGICAL', 1160 'argumentCount' => '1' 1161 ), 1162 'ISNA' => array( 1163 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1164 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NA', 1165 'argumentCount' => '1' 1166 ), 1167 'ISNONTEXT' => array( 1168 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1169 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NONTEXT', 1170 'argumentCount' => '1' 1171 ), 1172 'ISNUMBER' => array( 1173 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1174 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NUMBER', 1175 'argumentCount' => '1' 1176 ), 1177 'ISODD' => array( 1178 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1179 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ODD', 1180 'argumentCount' => '1' 1181 ), 1182 'ISPMT' => array( 1183 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1184 'functionCall' => 'PHPExcel_Calculation_Financial::ISPMT', 1185 'argumentCount' => '4' 1186 ), 1187 'ISREF' => array( 1188 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1189 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1190 'argumentCount' => '1' 1191 ), 1192 'ISTEXT' => array( 1193 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1194 'functionCall' => 'PHPExcel_Calculation_Functions::IS_TEXT', 1195 'argumentCount' => '1' 1196 ), 1197 'JIS' => array( 1198 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1199 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1200 'argumentCount' => '1' 1201 ), 1202 'KURT' => array( 1203 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1204 'functionCall' => 'PHPExcel_Calculation_Statistical::KURT', 1205 'argumentCount' => '1+' 1206 ), 1207 'LARGE' => array( 1208 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1209 'functionCall' => 'PHPExcel_Calculation_Statistical::LARGE', 1210 'argumentCount' => '2' 1211 ), 1212 'LCM' => array( 1213 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1214 'functionCall' => 'PHPExcel_Calculation_MathTrig::LCM', 1215 'argumentCount' => '1+' 1216 ), 1217 'LEFT' => array( 1218 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1219 'functionCall' => 'PHPExcel_Calculation_TextData::LEFT', 1220 'argumentCount' => '1,2' 1221 ), 1222 'LEFTB' => array( 1223 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1224 'functionCall' => 'PHPExcel_Calculation_TextData::LEFT', 1225 'argumentCount' => '1,2' 1226 ), 1227 'LEN' => array( 1228 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1229 'functionCall' => 'PHPExcel_Calculation_TextData::STRINGLENGTH', 1230 'argumentCount' => '1' 1231 ), 1232 'LENB' => array( 1233 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1234 'functionCall' => 'PHPExcel_Calculation_TextData::STRINGLENGTH', 1235 'argumentCount' => '1' 1236 ), 1237 'LINEST' => array( 1238 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1239 'functionCall' => 'PHPExcel_Calculation_Statistical::LINEST', 1240 'argumentCount' => '1-4' 1241 ), 1242 'LN' => array( 1243 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1244 'functionCall' => 'log', 1245 'argumentCount' => '1' 1246 ), 1247 'LOG' => array( 1248 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1249 'functionCall' => 'PHPExcel_Calculation_MathTrig::LOG_BASE', 1250 'argumentCount' => '1,2' 1251 ), 1252 'LOG10' => array( 1253 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1254 'functionCall' => 'log10', 1255 'argumentCount' => '1' 1256 ), 1257 'LOGEST' => array( 1258 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1259 'functionCall' => 'PHPExcel_Calculation_Statistical::LOGEST', 1260 'argumentCount' => '1-4' 1261 ), 1262 'LOGINV' => array( 1263 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1264 'functionCall' => 'PHPExcel_Calculation_Statistical::LOGINV', 1265 'argumentCount' => '3' 1266 ), 1267 'LOGNORMDIST' => array( 1268 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1269 'functionCall' => 'PHPExcel_Calculation_Statistical::LOGNORMDIST', 1270 'argumentCount' => '3' 1271 ), 1272 'LOOKUP' => array( 1273 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1274 'functionCall' => 'PHPExcel_Calculation_LookupRef::LOOKUP', 1275 'argumentCount' => '2,3' 1276 ), 1277 'LOWER' => array( 1278 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1279 'functionCall' => 'PHPExcel_Calculation_TextData::LOWERCASE', 1280 'argumentCount' => '1' 1281 ), 1282 'MATCH' => array( 1283 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1284 'functionCall' => 'PHPExcel_Calculation_LookupRef::MATCH', 1285 'argumentCount' => '2,3' 1286 ), 1287 'MAX' => array( 1288 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1289 'functionCall' => 'PHPExcel_Calculation_Statistical::MAX', 1290 'argumentCount' => '1+' 1291 ), 1292 'MAXA' => array( 1293 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1294 'functionCall' => 'PHPExcel_Calculation_Statistical::MAXA', 1295 'argumentCount' => '1+' 1296 ), 1297 'MAXIF' => array( 1298 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1299 'functionCall' => 'PHPExcel_Calculation_Statistical::MAXIF', 1300 'argumentCount' => '2+' 1301 ), 1302 'MDETERM' => array( 1303 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1304 'functionCall' => 'PHPExcel_Calculation_MathTrig::MDETERM', 1305 'argumentCount' => '1' 1306 ), 1307 'MDURATION' => array( 1308 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1309 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1310 'argumentCount' => '5,6' 1311 ), 1312 'MEDIAN' => array( 1313 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1314 'functionCall' => 'PHPExcel_Calculation_Statistical::MEDIAN', 1315 'argumentCount' => '1+' 1316 ), 1317 'MEDIANIF' => array( 1318 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1319 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1320 'argumentCount' => '2+' 1321 ), 1322 'MID' => array( 1323 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1324 'functionCall' => 'PHPExcel_Calculation_TextData::MID', 1325 'argumentCount' => '3' 1326 ), 1327 'MIDB' => array( 1328 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1329 'functionCall' => 'PHPExcel_Calculation_TextData::MID', 1330 'argumentCount' => '3' 1331 ), 1332 'MIN' => array( 1333 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1334 'functionCall' => 'PHPExcel_Calculation_Statistical::MIN', 1335 'argumentCount' => '1+' 1336 ), 1337 'MINA' => array( 1338 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1339 'functionCall' => 'PHPExcel_Calculation_Statistical::MINA', 1340 'argumentCount' => '1+' 1341 ), 1342 'MINIF' => array( 1343 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1344 'functionCall' => 'PHPExcel_Calculation_Statistical::MINIF', 1345 'argumentCount' => '2+' 1346 ), 1347 'MINUTE' => array( 1348 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1349 'functionCall' => 'PHPExcel_Calculation_DateTime::MINUTEOFHOUR', 1350 'argumentCount' => '1' 1351 ), 1352 'MINVERSE' => array( 1353 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1354 'functionCall' => 'PHPExcel_Calculation_MathTrig::MINVERSE', 1355 'argumentCount' => '1' 1356 ), 1357 'MIRR' => array( 1358 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1359 'functionCall' => 'PHPExcel_Calculation_Financial::MIRR', 1360 'argumentCount' => '3' 1361 ), 1362 'MMULT' => array( 1363 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1364 'functionCall' => 'PHPExcel_Calculation_MathTrig::MMULT', 1365 'argumentCount' => '2' 1366 ), 1367 'MOD' => array( 1368 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1369 'functionCall' => 'PHPExcel_Calculation_MathTrig::MOD', 1370 'argumentCount' => '2' 1371 ), 1372 'MODE' => array( 1373 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1374 'functionCall' => 'PHPExcel_Calculation_Statistical::MODE', 1375 'argumentCount' => '1+' 1376 ), 1377 'MONTH' => array( 1378 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1379 'functionCall' => 'PHPExcel_Calculation_DateTime::MONTHOFYEAR', 1380 'argumentCount' => '1' 1381 ), 1382 'MROUND' => array( 1383 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1384 'functionCall' => 'PHPExcel_Calculation_MathTrig::MROUND', 1385 'argumentCount' => '2' 1386 ), 1387 'MULTINOMIAL' => array( 1388 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1389 'functionCall' => 'PHPExcel_Calculation_MathTrig::MULTINOMIAL', 1390 'argumentCount' => '1+' 1391 ), 1392 'N' => array( 1393 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1394 'functionCall' => 'PHPExcel_Calculation_Functions::N', 1395 'argumentCount' => '1' 1396 ), 1397 'NA' => array( 1398 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1399 'functionCall' => 'PHPExcel_Calculation_Functions::NA', 1400 'argumentCount' => '0' 1401 ), 1402 'NEGBINOMDIST' => array( 1403 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1404 'functionCall' => 'PHPExcel_Calculation_Statistical::NEGBINOMDIST', 1405 'argumentCount' => '3' 1406 ), 1407 'NETWORKDAYS' => array( 1408 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1409 'functionCall' => 'PHPExcel_Calculation_DateTime::NETWORKDAYS', 1410 'argumentCount' => '2+' 1411 ), 1412 'NOMINAL' => array( 1413 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1414 'functionCall' => 'PHPExcel_Calculation_Financial::NOMINAL', 1415 'argumentCount' => '2' 1416 ), 1417 'NORMDIST' => array( 1418 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1419 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMDIST', 1420 'argumentCount' => '4' 1421 ), 1422 'NORMINV' => array( 1423 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1424 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMINV', 1425 'argumentCount' => '3' 1426 ), 1427 'NORMSDIST' => array( 1428 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1429 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMSDIST', 1430 'argumentCount' => '1' 1431 ), 1432 'NORMSINV' => array( 1433 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1434 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMSINV', 1435 'argumentCount' => '1' 1436 ), 1437 'NOT' => array( 1438 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 1439 'functionCall' => 'PHPExcel_Calculation_Logical::NOT', 1440 'argumentCount' => '1' 1441 ), 1442 'NOW' => array( 1443 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1444 'functionCall' => 'PHPExcel_Calculation_DateTime::DATETIMENOW', 1445 'argumentCount' => '0' 1446 ), 1447 'NPER' => array( 1448 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1449 'functionCall' => 'PHPExcel_Calculation_Financial::NPER', 1450 'argumentCount' => '3-5' 1451 ), 1452 'NPV' => array( 1453 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1454 'functionCall' => 'PHPExcel_Calculation_Financial::NPV', 1455 'argumentCount' => '2+' 1456 ), 1457 'OCT2BIN' => array( 1458 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1459 'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTOBIN', 1460 'argumentCount' => '1,2' 1461 ), 1462 'OCT2DEC' => array( 1463 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1464 'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTODEC', 1465 'argumentCount' => '1' 1466 ), 1467 'OCT2HEX' => array( 1468 'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING, 1469 'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTOHEX', 1470 'argumentCount' => '1,2' 1471 ), 1472 'ODD' => array( 1473 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1474 'functionCall' => 'PHPExcel_Calculation_MathTrig::ODD', 1475 'argumentCount' => '1' 1476 ), 1477 'ODDFPRICE' => array( 1478 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1479 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1480 'argumentCount' => '8,9' 1481 ), 1482 'ODDFYIELD' => array( 1483 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1484 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1485 'argumentCount' => '8,9' 1486 ), 1487 'ODDLPRICE' => array( 1488 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1489 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1490 'argumentCount' => '7,8' 1491 ), 1492 'ODDLYIELD' => array( 1493 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1494 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1495 'argumentCount' => '7,8' 1496 ), 1497 'OFFSET' => array( 1498 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1499 'functionCall' => 'PHPExcel_Calculation_LookupRef::OFFSET', 1500 'argumentCount' => '3-5', 1501 'passCellReference' => true, 1502 'passByReference' => array(true) 1503 ), 1504 'OR' => array( 1505 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 1506 'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_OR', 1507 'argumentCount' => '1+' 1508 ), 1509 'PEARSON' => array( 1510 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1511 'functionCall' => 'PHPExcel_Calculation_Statistical::CORREL', 1512 'argumentCount' => '2' 1513 ), 1514 'PERCENTILE' => array( 1515 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1516 'functionCall' => 'PHPExcel_Calculation_Statistical::PERCENTILE', 1517 'argumentCount' => '2' 1518 ), 1519 'PERCENTRANK' => array( 1520 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1521 'functionCall' => 'PHPExcel_Calculation_Statistical::PERCENTRANK', 1522 'argumentCount' => '2,3' 1523 ), 1524 'PERMUT' => array( 1525 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1526 'functionCall' => 'PHPExcel_Calculation_Statistical::PERMUT', 1527 'argumentCount' => '2' 1528 ), 1529 'PHONETIC' => array( 1530 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1531 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1532 'argumentCount' => '1' 1533 ), 1534 'PI' => array( 1535 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1536 'functionCall' => 'pi', 1537 'argumentCount' => '0' 1538 ), 1539 'PMT' => array( 1540 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1541 'functionCall' => 'PHPExcel_Calculation_Financial::PMT', 1542 'argumentCount' => '3-5' 1543 ), 1544 'POISSON' => array( 1545 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1546 'functionCall' => 'PHPExcel_Calculation_Statistical::POISSON', 1547 'argumentCount' => '3' 1548 ), 1549 'POWER' => array( 1550 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1551 'functionCall' => 'PHPExcel_Calculation_MathTrig::POWER', 1552 'argumentCount' => '2' 1553 ), 1554 'PPMT' => array( 1555 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1556 'functionCall' => 'PHPExcel_Calculation_Financial::PPMT', 1557 'argumentCount' => '4-6' 1558 ), 1559 'PRICE' => array( 1560 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1561 'functionCall' => 'PHPExcel_Calculation_Financial::PRICE', 1562 'argumentCount' => '6,7' 1563 ), 1564 'PRICEDISC' => array( 1565 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1566 'functionCall' => 'PHPExcel_Calculation_Financial::PRICEDISC', 1567 'argumentCount' => '4,5' 1568 ), 1569 'PRICEMAT' => array( 1570 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1571 'functionCall' => 'PHPExcel_Calculation_Financial::PRICEMAT', 1572 'argumentCount' => '5,6' 1573 ), 1574 'PROB' => array( 1575 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1576 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1577 'argumentCount' => '3,4' 1578 ), 1579 'PRODUCT' => array( 1580 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1581 'functionCall' => 'PHPExcel_Calculation_MathTrig::PRODUCT', 1582 'argumentCount' => '1+' 1583 ), 1584 'PROPER' => array( 1585 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1586 'functionCall' => 'PHPExcel_Calculation_TextData::PROPERCASE', 1587 'argumentCount' => '1' 1588 ), 1589 'PV' => array( 1590 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1591 'functionCall' => 'PHPExcel_Calculation_Financial::PV', 1592 'argumentCount' => '3-5' 1593 ), 1594 'QUARTILE' => array( 1595 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1596 'functionCall' => 'PHPExcel_Calculation_Statistical::QUARTILE', 1597 'argumentCount' => '2' 1598 ), 1599 'QUOTIENT' => array( 1600 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1601 'functionCall' => 'PHPExcel_Calculation_MathTrig::QUOTIENT', 1602 'argumentCount' => '2' 1603 ), 1604 'RADIANS' => array( 1605 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1606 'functionCall' => 'deg2rad', 1607 'argumentCount' => '1' 1608 ), 1609 'RAND' => array( 1610 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1611 'functionCall' => 'PHPExcel_Calculation_MathTrig::RAND', 1612 'argumentCount' => '0' 1613 ), 1614 'RANDBETWEEN' => array( 1615 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1616 'functionCall' => 'PHPExcel_Calculation_MathTrig::RAND', 1617 'argumentCount' => '2' 1618 ), 1619 'RANK' => array( 1620 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1621 'functionCall' => 'PHPExcel_Calculation_Statistical::RANK', 1622 'argumentCount' => '2,3' 1623 ), 1624 'RATE' => array( 1625 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1626 'functionCall' => 'PHPExcel_Calculation_Financial::RATE', 1627 'argumentCount' => '3-6' 1628 ), 1629 'RECEIVED' => array( 1630 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1631 'functionCall' => 'PHPExcel_Calculation_Financial::RECEIVED', 1632 'argumentCount' => '4-5' 1633 ), 1634 'REPLACE' => array( 1635 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1636 'functionCall' => 'PHPExcel_Calculation_TextData::REPLACE', 1637 'argumentCount' => '4' 1638 ), 1639 'REPLACEB' => array( 1640 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1641 'functionCall' => 'PHPExcel_Calculation_TextData::REPLACE', 1642 'argumentCount' => '4' 1643 ), 1644 'REPT' => array( 1645 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1646 'functionCall' => 'str_repeat', 1647 'argumentCount' => '2' 1648 ), 1649 'RIGHT' => array( 1650 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1651 'functionCall' => 'PHPExcel_Calculation_TextData::RIGHT', 1652 'argumentCount' => '1,2' 1653 ), 1654 'RIGHTB' => array( 1655 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1656 'functionCall' => 'PHPExcel_Calculation_TextData::RIGHT', 1657 'argumentCount' => '1,2' 1658 ), 1659 'ROMAN' => array( 1660 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1661 'functionCall' => 'PHPExcel_Calculation_MathTrig::ROMAN', 1662 'argumentCount' => '1,2' 1663 ), 1664 'ROUND' => array( 1665 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1666 'functionCall' => 'round', 1667 'argumentCount' => '2' 1668 ), 1669 'ROUNDDOWN' => array( 1670 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1671 'functionCall' => 'PHPExcel_Calculation_MathTrig::ROUNDDOWN', 1672 'argumentCount' => '2' 1673 ), 1674 'ROUNDUP' => array( 1675 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1676 'functionCall' => 'PHPExcel_Calculation_MathTrig::ROUNDUP', 1677 'argumentCount' => '2' 1678 ), 1679 'ROW' => array( 1680 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1681 'functionCall' => 'PHPExcel_Calculation_LookupRef::ROW', 1682 'argumentCount' => '-1', 1683 'passByReference' => array(true) 1684 ), 1685 'ROWS' => array( 1686 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1687 'functionCall' => 'PHPExcel_Calculation_LookupRef::ROWS', 1688 'argumentCount' => '1' 1689 ), 1690 'RSQ' => array( 1691 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1692 'functionCall' => 'PHPExcel_Calculation_Statistical::RSQ', 1693 'argumentCount' => '2' 1694 ), 1695 'RTD' => array( 1696 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1697 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1698 'argumentCount' => '1+' 1699 ), 1700 'SEARCH' => array( 1701 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1702 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE', 1703 'argumentCount' => '2,3' 1704 ), 1705 'SEARCHB' => array( 1706 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1707 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE', 1708 'argumentCount' => '2,3' 1709 ), 1710 'SECOND' => array( 1711 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1712 'functionCall' => 'PHPExcel_Calculation_DateTime::SECONDOFMINUTE', 1713 'argumentCount' => '1' 1714 ), 1715 'SERIESSUM' => array( 1716 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1717 'functionCall' => 'PHPExcel_Calculation_MathTrig::SERIESSUM', 1718 'argumentCount' => '4' 1719 ), 1720 'SIGN' => array( 1721 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1722 'functionCall' => 'PHPExcel_Calculation_MathTrig::SIGN', 1723 'argumentCount' => '1' 1724 ), 1725 'SIN' => array( 1726 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1727 'functionCall' => 'sin', 1728 'argumentCount' => '1' 1729 ), 1730 'SINH' => array( 1731 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1732 'functionCall' => 'sinh', 1733 'argumentCount' => '1' 1734 ), 1735 'SKEW' => array( 1736 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1737 'functionCall' => 'PHPExcel_Calculation_Statistical::SKEW', 1738 'argumentCount' => '1+' 1739 ), 1740 'SLN' => array( 1741 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1742 'functionCall' => 'PHPExcel_Calculation_Financial::SLN', 1743 'argumentCount' => '3' 1744 ), 1745 'SLOPE' => array( 1746 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1747 'functionCall' => 'PHPExcel_Calculation_Statistical::SLOPE', 1748 'argumentCount' => '2' 1749 ), 1750 'SMALL' => array( 1751 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1752 'functionCall' => 'PHPExcel_Calculation_Statistical::SMALL', 1753 'argumentCount' => '2' 1754 ), 1755 'SQRT' => array( 1756 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1757 'functionCall' => 'sqrt', 1758 'argumentCount' => '1' 1759 ), 1760 'SQRTPI' => array( 1761 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1762 'functionCall' => 'PHPExcel_Calculation_MathTrig::SQRTPI', 1763 'argumentCount' => '1' 1764 ), 1765 'STANDARDIZE' => array( 1766 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1767 'functionCall' => 'PHPExcel_Calculation_Statistical::STANDARDIZE', 1768 'argumentCount' => '3' 1769 ), 1770 'STDEV' => array( 1771 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1772 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEV', 1773 'argumentCount' => '1+' 1774 ), 1775 'STDEVA' => array( 1776 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1777 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVA', 1778 'argumentCount' => '1+' 1779 ), 1780 'STDEVP' => array( 1781 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1782 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVP', 1783 'argumentCount' => '1+' 1784 ), 1785 'STDEVPA' => array( 1786 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1787 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVPA', 1788 'argumentCount' => '1+' 1789 ), 1790 'STEYX' => array( 1791 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1792 'functionCall' => 'PHPExcel_Calculation_Statistical::STEYX', 1793 'argumentCount' => '2' 1794 ), 1795 'SUBSTITUTE' => array( 1796 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1797 'functionCall' => 'PHPExcel_Calculation_TextData::SUBSTITUTE', 1798 'argumentCount' => '3,4' 1799 ), 1800 'SUBTOTAL' => array( 1801 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1802 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUBTOTAL', 1803 'argumentCount' => '2+' 1804 ), 1805 'SUM' => array( 1806 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1807 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUM', 1808 'argumentCount' => '1+' 1809 ), 1810 'SUMIF' => array( 1811 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1812 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMIF', 1813 'argumentCount' => '2,3' 1814 ), 1815 'SUMIFS' => array( 1816 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1817 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMIFS', 1818 'argumentCount' => '3+' 1819 ), 1820 'SUMPRODUCT' => array( 1821 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1822 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMPRODUCT', 1823 'argumentCount' => '1+' 1824 ), 1825 'SUMSQ' => array( 1826 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1827 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMSQ', 1828 'argumentCount' => '1+' 1829 ), 1830 'SUMX2MY2' => array( 1831 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1832 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMX2MY2', 1833 'argumentCount' => '2' 1834 ), 1835 'SUMX2PY2' => array( 1836 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1837 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMX2PY2', 1838 'argumentCount' => '2' 1839 ), 1840 'SUMXMY2' => array( 1841 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1842 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMXMY2', 1843 'argumentCount' => '2' 1844 ), 1845 'SYD' => array( 1846 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1847 'functionCall' => 'PHPExcel_Calculation_Financial::SYD', 1848 'argumentCount' => '4' 1849 ), 1850 'T' => array( 1851 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1852 'functionCall' => 'PHPExcel_Calculation_TextData::RETURNSTRING', 1853 'argumentCount' => '1' 1854 ), 1855 'TAN' => array( 1856 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1857 'functionCall' => 'tan', 1858 'argumentCount' => '1' 1859 ), 1860 'TANH' => array( 1861 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1862 'functionCall' => 'tanh', 1863 'argumentCount' => '1' 1864 ), 1865 'TBILLEQ' => array( 1866 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1867 'functionCall' => 'PHPExcel_Calculation_Financial::TBILLEQ', 1868 'argumentCount' => '3' 1869 ), 1870 'TBILLPRICE' => array( 1871 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1872 'functionCall' => 'PHPExcel_Calculation_Financial::TBILLPRICE', 1873 'argumentCount' => '3' 1874 ), 1875 'TBILLYIELD' => array( 1876 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1877 'functionCall' => 'PHPExcel_Calculation_Financial::TBILLYIELD', 1878 'argumentCount' => '3' 1879 ), 1880 'TDIST' => array( 1881 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1882 'functionCall' => 'PHPExcel_Calculation_Statistical::TDIST', 1883 'argumentCount' => '3' 1884 ), 1885 'TEXT' => array( 1886 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1887 'functionCall' => 'PHPExcel_Calculation_TextData::TEXTFORMAT', 1888 'argumentCount' => '2' 1889 ), 1890 'TIME' => array( 1891 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1892 'functionCall' => 'PHPExcel_Calculation_DateTime::TIME', 1893 'argumentCount' => '3' 1894 ), 1895 'TIMEVALUE' => array( 1896 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1897 'functionCall' => 'PHPExcel_Calculation_DateTime::TIMEVALUE', 1898 'argumentCount' => '1' 1899 ), 1900 'TINV' => array( 1901 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1902 'functionCall' => 'PHPExcel_Calculation_Statistical::TINV', 1903 'argumentCount' => '2' 1904 ), 1905 'TODAY' => array( 1906 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 1907 'functionCall' => 'PHPExcel_Calculation_DateTime::DATENOW', 1908 'argumentCount' => '0' 1909 ), 1910 'TRANSPOSE' => array( 1911 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1912 'functionCall' => 'PHPExcel_Calculation_LookupRef::TRANSPOSE', 1913 'argumentCount' => '1' 1914 ), 1915 'TREND' => array( 1916 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1917 'functionCall' => 'PHPExcel_Calculation_Statistical::TREND', 1918 'argumentCount' => '1-4' 1919 ), 1920 'TRIM' => array( 1921 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1922 'functionCall' => 'PHPExcel_Calculation_TextData::TRIMSPACES', 1923 'argumentCount' => '1' 1924 ), 1925 'TRIMMEAN' => array( 1926 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1927 'functionCall' => 'PHPExcel_Calculation_Statistical::TRIMMEAN', 1928 'argumentCount' => '2' 1929 ), 1930 'TRUE' => array( 1931 'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 1932 'functionCall' => 'PHPExcel_Calculation_Logical::TRUE', 1933 'argumentCount' => '0' 1934 ), 1935 'TRUNC' => array( 1936 'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG, 1937 'functionCall' => 'PHPExcel_Calculation_MathTrig::TRUNC', 1938 'argumentCount' => '1,2' 1939 ), 1940 'TTEST' => array( 1941 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1942 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1943 'argumentCount' => '4' 1944 ), 1945 'TYPE' => array( 1946 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1947 'functionCall' => 'PHPExcel_Calculation_Functions::TYPE', 1948 'argumentCount' => '1' 1949 ), 1950 'UPPER' => array( 1951 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1952 'functionCall' => 'PHPExcel_Calculation_TextData::UPPERCASE', 1953 'argumentCount' => '1' 1954 ), 1955 'USDOLLAR' => array( 1956 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1957 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1958 'argumentCount' => '2' 1959 ), 1960 'VALUE' => array( 1961 'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA, 1962 'functionCall' => 'PHPExcel_Calculation_TextData::VALUE', 1963 'argumentCount' => '1' 1964 ), 1965 'VAR' => array( 1966 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1967 'functionCall' => 'PHPExcel_Calculation_Statistical::VARFunc', 1968 'argumentCount' => '1+' 1969 ), 1970 'VARA' => array( 1971 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1972 'functionCall' => 'PHPExcel_Calculation_Statistical::VARA', 1973 'argumentCount' => '1+' 1974 ), 1975 'VARP' => array( 1976 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1977 'functionCall' => 'PHPExcel_Calculation_Statistical::VARP', 1978 'argumentCount' => '1+' 1979 ), 1980 'VARPA' => array( 1981 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 1982 'functionCall' => 'PHPExcel_Calculation_Statistical::VARPA', 1983 'argumentCount' => '1+' 1984 ), 1985 'VDB' => array( 1986 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 1987 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 1988 'argumentCount' => '5-7' 1989 ), 1990 'VERSION' => array( 1991 'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 1992 'functionCall' => 'PHPExcel_Calculation_Functions::VERSION', 1993 'argumentCount' => '0' 1994 ), 1995 'VLOOKUP' => array( 1996 'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 1997 'functionCall' => 'PHPExcel_Calculation_LookupRef::VLOOKUP', 1998 'argumentCount' => '3,4' 1999 ), 2000 'WEEKDAY' => array( 2001 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 2002 'functionCall' => 'PHPExcel_Calculation_DateTime::DAYOFWEEK', 2003 'argumentCount' => '1,2' 2004 ), 2005 'WEEKNUM' => array( 2006 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 2007 'functionCall' => 'PHPExcel_Calculation_DateTime::WEEKOFYEAR', 2008 'argumentCount' => '1,2' 2009 ), 2010 'WEIBULL' => array( 2011 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 2012 'functionCall' => 'PHPExcel_Calculation_Statistical::WEIBULL', 2013 'argumentCount' => '4' 2014 ), 2015 'WORKDAY' => array( 2016 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 2017 'functionCall' => 'PHPExcel_Calculation_DateTime::WORKDAY', 2018 'argumentCount' => '2+' 2019 ), 2020 'XIRR' => array( 2021 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 2022 'functionCall' => 'PHPExcel_Calculation_Financial::XIRR', 2023 'argumentCount' => '2,3' 2024 ), 2025 'XNPV' => array( 2026 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 2027 'functionCall' => 'PHPExcel_Calculation_Financial::XNPV', 2028 'argumentCount' => '3' 2029 ), 2030 'YEAR' => array( 2031 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 2032 'functionCall' => 'PHPExcel_Calculation_DateTime::YEAR', 2033 'argumentCount' => '1' 2034 ), 2035 'YEARFRAC' => array( 2036 'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME, 2037 'functionCall' => 'PHPExcel_Calculation_DateTime::YEARFRAC', 2038 'argumentCount' => '2,3' 2039 ), 2040 'YIELD' => array( 2041 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 2042 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', 2043 'argumentCount' => '6,7' 2044 ), 2045 'YIELDDISC' => array( 2046 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 2047 'functionCall' => 'PHPExcel_Calculation_Financial::YIELDDISC', 2048 'argumentCount' => '4,5' 2049 ), 2050 'YIELDMAT' => array( 2051 'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL, 2052 'functionCall' => 'PHPExcel_Calculation_Financial::YIELDMAT', 2053 'argumentCount' => '5,6' 2054 ), 2055 'ZTEST' => array( 2056 'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 2057 'functionCall' => 'PHPExcel_Calculation_Statistical::ZTEST', 2058 'argumentCount' => '2-3' 2059 ) 2060 ); 2061 2062 // Internal functions used for special control purposes 2063 private static $controlFunctions = array( 2064 'MKMATRIX' => array( 2065 'argumentCount' => '*', 2066 'functionCall' => 'self::mkMatrix' 2067 ) 2068 ); 2069 2070 2071 public function __construct(PHPExcel $workbook = null) 2072 { 2073 $this->delta = 1 * pow(10, 0 - ini_get('precision')); 2074 2075 $this->workbook = $workbook; 2076 $this->cyclicReferenceStack = new PHPExcel_CalcEngine_CyclicReferenceStack(); 2077 $this->_debugLog = new PHPExcel_CalcEngine_Logger($this->cyclicReferenceStack); 2078 } 2079 2080 2081 private static function loadLocales() 2082 { 2083 $localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/'; 2084 foreach (glob($localeFileDirectory.'/*', GLOB_ONLYDIR) as $filename) { 2085 $filename = substr($filename, strlen($localeFileDirectory)+1); 2086 if ($filename != 'en') { 2087 self::$validLocaleLanguages[] = $filename; 2088 } 2089 } 2090 } 2091 2092 /** 2093 * Get an instance of this class 2094 * 2095 * @access public 2096 * @param PHPExcel $workbook Injected workbook for working with a PHPExcel object, 2097 * or NULL to create a standalone claculation engine 2098 * @return PHPExcel_Calculation 2099 */ 2100 public static function getInstance(PHPExcel $workbook = null) 2101 { 2102 if ($workbook !== null) { 2103 $instance = $workbook->getCalculationEngine(); 2104 if (isset($instance)) { 2105 return $instance; 2106 } 2107 } 2108 2109 if (!isset(self::$instance) || (self::$instance === null)) { 2110 self::$instance = new PHPExcel_Calculation(); 2111 } 2112 return self::$instance; 2113 } 2114 2115 /** 2116 * Unset an instance of this class 2117 * 2118 * @access public 2119 */ 2120 public function __destruct() 2121 { 2122 $this->workbook = null; 2123 } 2124 2125 /** 2126 * Flush the calculation cache for any existing instance of this class 2127 * but only if a PHPExcel_Calculation instance exists 2128 * 2129 * @access public 2130 * @return null 2131 */ 2132 public function flushInstance() 2133 { 2134 $this->clearCalculationCache(); 2135 } 2136 2137 2138 /** 2139 * Get the debuglog for this claculation engine instance 2140 * 2141 * @access public 2142 * @return PHPExcel_CalcEngine_Logger 2143 */ 2144 public function getDebugLog() 2145 { 2146 return $this->_debugLog; 2147 } 2148 2149 /** 2150 * __clone implementation. Cloning should not be allowed in a Singleton! 2151 * 2152 * @access public 2153 * @throws PHPExcel_Calculation_Exception 2154 */ 2155 final public function __clone() 2156 { 2157 throw new PHPExcel_Calculation_Exception('Cloning the calculation engine is not allowed!'); 2158 } 2159 2160 2161 /** 2162 * Return the locale-specific translation of TRUE 2163 * 2164 * @access public 2165 * @return string locale-specific translation of TRUE 2166 */ 2167 public static function getTRUE() 2168 { 2169 return self::$localeBoolean['TRUE']; 2170 } 2171 2172 /** 2173 * Return the locale-specific translation of FALSE 2174 * 2175 * @access public 2176 * @return string locale-specific translation of FALSE 2177 */ 2178 public static function getFALSE() 2179 { 2180 return self::$localeBoolean['FALSE']; 2181 } 2182 2183 /** 2184 * Set the Array Return Type (Array or Value of first element in the array) 2185 * 2186 * @access public 2187 * @param string $returnType Array return type 2188 * @return boolean Success or failure 2189 */ 2190 public static function setArrayReturnType($returnType) 2191 { 2192 if (($returnType == self::RETURN_ARRAY_AS_VALUE) || 2193 ($returnType == self::RETURN_ARRAY_AS_ERROR) || 2194 ($returnType == self::RETURN_ARRAY_AS_ARRAY)) { 2195 self::$returnArrayAsType = $returnType; 2196 return true; 2197 } 2198 return false; 2199 } 2200 2201 2202 /** 2203 * Return the Array Return Type (Array or Value of first element in the array) 2204 * 2205 * @access public 2206 * @return string $returnType Array return type 2207 */ 2208 public static function getArrayReturnType() 2209 { 2210 return self::$returnArrayAsType; 2211 } 2212 2213 2214 /** 2215 * Is calculation caching enabled? 2216 * 2217 * @access public 2218 * @return boolean 2219 */ 2220 public function getCalculationCacheEnabled() 2221 { 2222 return $this->calculationCacheEnabled; 2223 } 2224 2225 /** 2226 * Enable/disable calculation cache 2227 * 2228 * @access public 2229 * @param boolean $pValue 2230 */ 2231 public function setCalculationCacheEnabled($pValue = true) 2232 { 2233 $this->calculationCacheEnabled = $pValue; 2234 $this->clearCalculationCache(); 2235 } 2236 2237 2238 /** 2239 * Enable calculation cache 2240 */ 2241 public function enableCalculationCache() 2242 { 2243 $this->setCalculationCacheEnabled(true); 2244 } 2245 2246 2247 /** 2248 * Disable calculation cache 2249 */ 2250 public function disableCalculationCache() 2251 { 2252 $this->setCalculationCacheEnabled(false); 2253 } 2254 2255 2256 /** 2257 * Clear calculation cache 2258 */ 2259 public function clearCalculationCache() 2260 { 2261 $this->calculationCache = array(); 2262 } 2263 2264 /** 2265 * Clear calculation cache for a specified worksheet 2266 * 2267 * @param string $worksheetName 2268 */ 2269 public function clearCalculationCacheForWorksheet($worksheetName) 2270 { 2271 if (isset($this->calculationCache[$worksheetName])) { 2272 unset($this->calculationCache[$worksheetName]); 2273 } 2274 } 2275 2276 /** 2277 * Rename calculation cache for a specified worksheet 2278 * 2279 * @param string $fromWorksheetName 2280 * @param string $toWorksheetName 2281 */ 2282 public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName) 2283 { 2284 if (isset($this->calculationCache[$fromWorksheetName])) { 2285 $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName]; 2286 unset($this->calculationCache[$fromWorksheetName]); 2287 } 2288 } 2289 2290 2291 /** 2292 * Get the currently defined locale code 2293 * 2294 * @return string 2295 */ 2296 public function getLocale() 2297 { 2298 return self::$localeLanguage; 2299 } 2300 2301 2302 /** 2303 * Set the locale code 2304 * 2305 * @param string $locale The locale to use for formula translation 2306 * @return boolean 2307 */ 2308 public function setLocale($locale = 'en_us') 2309 { 2310 // Identify our locale and language 2311 $language = $locale = strtolower($locale); 2312 if (strpos($locale, '_') !== false) { 2313 list($language) = explode('_', $locale); 2314 } 2315 2316 if (count(self::$validLocaleLanguages) == 1) { 2317 self::loadLocales(); 2318 } 2319 // Test whether we have any language data for this language (any locale) 2320 if (in_array($language, self::$validLocaleLanguages)) { 2321 // initialise language/locale settings 2322 self::$localeFunctions = array(); 2323 self::$localeArgumentSeparator = ','; 2324 self::$localeBoolean = array('TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL'); 2325 // Default is English, if user isn't requesting english, then read the necessary data from the locale files 2326 if ($locale != 'en_us') { 2327 // Search for a file with a list of function names for locale 2328 $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'functions'; 2329 if (!file_exists($functionNamesFile)) { 2330 // If there isn't a locale specific function file, look for a language specific function file 2331 $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions'; 2332 if (!file_exists($functionNamesFile)) { 2333 return false; 2334 } 2335 } 2336 // Retrieve the list of locale or language specific function names 2337 $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 2338 foreach ($localeFunctions as $localeFunction) { 2339 list($localeFunction) = explode('##', $localeFunction); // Strip out comments 2340 if (strpos($localeFunction, '=') !== false) { 2341 list($fName, $lfName) = explode('=', $localeFunction); 2342 $fName = trim($fName); 2343 $lfName = trim($lfName); 2344 if ((isset(self::$PHPExcelFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) { 2345 self::$localeFunctions[$fName] = $lfName; 2346 } 2347 } 2348 } 2349 // Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions 2350 if (isset(self::$localeFunctions['TRUE'])) { 2351 self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE']; 2352 } 2353 if (isset(self::$localeFunctions['FALSE'])) { 2354 self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE']; 2355 } 2356 2357 $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'config'; 2358 if (!file_exists($configFile)) { 2359 $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'config'; 2360 } 2361 if (file_exists($configFile)) { 2362 $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 2363 foreach ($localeSettings as $localeSetting) { 2364 list($localeSetting) = explode('##', $localeSetting); // Strip out comments 2365 if (strpos($localeSetting, '=') !== false) { 2366 list($settingName, $settingValue) = explode('=', $localeSetting); 2367 $settingName = strtoupper(trim($settingName)); 2368 switch ($settingName) { 2369 case 'ARGUMENTSEPARATOR': 2370 self::$localeArgumentSeparator = trim($settingValue); 2371 break; 2372 } 2373 } 2374 } 2375 } 2376 } 2377 2378 self::$functionReplaceFromExcel = self::$functionReplaceToExcel = 2379 self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null; 2380 self::$localeLanguage = $locale; 2381 return true; 2382 } 2383 return false; 2384 } 2385 2386 2387 2388 public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces) 2389 { 2390 $strlen = mb_strlen($formula); 2391 for ($i = 0; $i < $strlen; ++$i) { 2392 $chr = mb_substr($formula, $i, 1); 2393 switch ($chr) { 2394 case '{': 2395 $inBraces = true; 2396 break; 2397 case '}': 2398 $inBraces = false; 2399 break; 2400 case $fromSeparator: 2401 if (!$inBraces) { 2402 $formula = mb_substr($formula, 0, $i).$toSeparator.mb_substr($formula, $i+1); 2403 } 2404 } 2405 } 2406 return $formula; 2407 } 2408 2409 private static function translateFormula($from, $to, $formula, $fromSeparator, $toSeparator) 2410 { 2411 // Convert any Excel function names to the required language 2412 if (self::$localeLanguage !== 'en_us') { 2413 $inBraces = false; 2414 // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators 2415 if (strpos($formula, '"') !== false) { 2416 // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded 2417 // the formula 2418 $temp = explode('"', $formula); 2419 $i = false; 2420 foreach ($temp as &$value) { 2421 // Only count/replace in alternating array entries 2422 if ($i = !$i) { 2423 $value = preg_replace($from, $to, $value); 2424 $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces); 2425 } 2426 } 2427 unset($value); 2428 // Then rebuild the formula string 2429 $formula = implode('"', $temp); 2430 } else { 2431 // If there's no quoted strings, then we do a simple count/replace 2432 $formula = preg_replace($from, $to, $formula); 2433 $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inBraces); 2434 } 2435 } 2436 2437 return $formula; 2438 } 2439 2440 private static $functionReplaceFromExcel = null; 2441 private static $functionReplaceToLocale = null; 2442 2443 public function _translateFormulaToLocale($formula) 2444 { 2445 if (self::$functionReplaceFromExcel === null) { 2446 self::$functionReplaceFromExcel = array(); 2447 foreach (array_keys(self::$localeFunctions) as $excelFunctionName) { 2448 self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui'; 2449 } 2450 foreach (array_keys(self::$localeBoolean) as $excelBoolean) { 2451 self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui'; 2452 } 2453 2454 } 2455 2456 if (self::$functionReplaceToLocale === null) { 2457 self::$functionReplaceToLocale = array(); 2458 foreach (array_values(self::$localeFunctions) as $localeFunctionName) { 2459 self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2'; 2460 } 2461 foreach (array_values(self::$localeBoolean) as $localeBoolean) { 2462 self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2'; 2463 } 2464 } 2465 2466 return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator); 2467 } 2468 2469 2470 private static $functionReplaceFromLocale = null; 2471 private static $functionReplaceToExcel = null; 2472 2473 public function _translateFormulaToEnglish($formula) 2474 { 2475 if (self::$functionReplaceFromLocale === null) { 2476 self::$functionReplaceFromLocale = array(); 2477 foreach (array_values(self::$localeFunctions) as $localeFunctionName) { 2478 self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui'; 2479 } 2480 foreach (array_values(self::$localeBoolean) as $excelBoolean) { 2481 self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui'; 2482 } 2483 } 2484 2485 if (self::$functionReplaceToExcel === null) { 2486 self::$functionReplaceToExcel = array(); 2487 foreach (array_keys(self::$localeFunctions) as $excelFunctionName) { 2488 self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2'; 2489 } 2490 foreach (array_keys(self::$localeBoolean) as $excelBoolean) { 2491 self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2'; 2492 } 2493 } 2494 2495 return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ','); 2496 } 2497 2498 2499 public static function localeFunc($function) 2500 { 2501 if (self::$localeLanguage !== 'en_us') { 2502 $functionName = trim($function, '('); 2503 if (isset(self::$localeFunctions[$functionName])) { 2504 $brace = ($functionName != $function); 2505 $function = self::$localeFunctions[$functionName]; 2506 if ($brace) { 2507 $function .= '('; 2508 } 2509 } 2510 } 2511 return $function; 2512 } 2513 2514 2515 2516 2517 /** 2518 * Wrap string values in quotes 2519 * 2520 * @param mixed $value 2521 * @return mixed 2522 */ 2523 public static function wrapResult($value) 2524 { 2525 if (is_string($value)) { 2526 // Error values cannot be "wrapped" 2527 if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) { 2528 // Return Excel errors "as is" 2529 return $value; 2530 } 2531 // Return strings wrapped in quotes 2532 return '"'.$value.'"'; 2533 // Convert numeric errors to NaN error 2534 } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) { 2535 return PHPExcel_Calculation_Functions::NaN(); 2536 } 2537 2538 return $value; 2539 } 2540 2541 2542 /** 2543 * Remove quotes used as a wrapper to identify string values 2544 * 2545 * @param mixed $value 2546 * @return mixed 2547 */ 2548 public static function unwrapResult($value) 2549 { 2550 if (is_string($value)) { 2551 if ((isset($value{0})) && ($value{0} == '"') && (substr($value, -1) == '"')) { 2552 return substr($value, 1, -1); 2553 } 2554 // Convert numeric errors to NaN error 2555 } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) { 2556 return PHPExcel_Calculation_Functions::NaN(); 2557 } 2558 return $value; 2559 } 2560 2561 2562 2563 2564 /** 2565 * Calculate cell value (using formula from a cell ID) 2566 * Retained for backward compatibility 2567 * 2568 * @access public 2569 * @param PHPExcel_Cell $pCell Cell to calculate 2570 * @return mixed 2571 * @throws PHPExcel_Calculation_Exception 2572 */ 2573 public function calculate(PHPExcel_Cell $pCell = null) 2574 { 2575 try { 2576 return $this->calculateCellValue($pCell); 2577 } catch (PHPExcel_Exception $e) { 2578 throw new PHPExcel_Calculation_Exception($e->getMessage()); 2579 } 2580 } 2581 2582 2583 /** 2584 * Calculate the value of a cell formula 2585 * 2586 * @access public 2587 * @param PHPExcel_Cell $pCell Cell to calculate 2588 * @param Boolean $resetLog Flag indicating whether the debug log should be reset or not 2589 * @return mixed 2590 * @throws PHPExcel_Calculation_Exception 2591 */ 2592 public function calculateCellValue(PHPExcel_Cell $pCell = null, $resetLog = true) 2593 { 2594 if ($pCell === null) { 2595 return null; 2596 } 2597 2598 $returnArrayAsType = self::$returnArrayAsType; 2599 if ($resetLog) { 2600 // Initialise the logging settings if requested 2601 $this->formulaError = null; 2602 $this->_debugLog->clearLog(); 2603 $this->cyclicReferenceStack->clear(); 2604 $this->cyclicFormulaCounter = 1; 2605 2606 self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY; 2607 } 2608 2609 // Execute the calculation for the cell formula 2610 $this->cellStack[] = array( 2611 'sheet' => $pCell->getWorksheet()->getTitle(), 2612 'cell' => $pCell->getCoordinate(), 2613 ); 2614 try { 2615 $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell)); 2616 $cellAddress = array_pop($this->cellStack); 2617 $this->workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']); 2618 } catch (PHPExcel_Exception $e) { 2619 $cellAddress = array_pop($this->cellStack); 2620 $this->workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']); 2621 throw new PHPExcel_Calculation_Exception($e->getMessage()); 2622 } 2623 2624 if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) { 2625 self::$returnArrayAsType = $returnArrayAsType; 2626 $testResult = PHPExcel_Calculation_Functions::flattenArray($result); 2627 if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) { 2628 return PHPExcel_Calculation_Functions::VALUE(); 2629 } 2630 // If there's only a single cell in the array, then we allow it 2631 if (count($testResult) != 1) { 2632 // If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it 2633 $r = array_keys($result); 2634 $r = array_shift($r); 2635 if (!is_numeric($r)) { 2636 return PHPExcel_Calculation_Functions::VALUE(); 2637 } 2638 if (is_array($result[$r])) { 2639 $c = array_keys($result[$r]); 2640 $c = array_shift($c); 2641 if (!is_numeric($c)) { 2642 return PHPExcel_Calculation_Functions::VALUE(); 2643 } 2644 } 2645 } 2646 $result = array_shift($testResult); 2647 } 2648 self::$returnArrayAsType = $returnArrayAsType; 2649 2650 2651 if ($result === null) { 2652 return 0; 2653 } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) { 2654 return PHPExcel_Calculation_Functions::NaN(); 2655 } 2656 return $result; 2657 } 2658 2659 2660 /** 2661 * Validate and parse a formula string 2662 * 2663 * @param string $formula Formula to parse 2664 * @return array 2665 * @throws PHPExcel_Calculation_Exception 2666 */ 2667 public function parseFormula($formula) 2668 { 2669 // Basic validation that this is indeed a formula 2670 // We return an empty array if not 2671 $formula = trim($formula); 2672 if ((!isset($formula{0})) || ($formula{0} != '=')) { 2673 return array(); 2674 } 2675 $formula = ltrim(substr($formula, 1)); 2676 if (!isset($formula{0})) { 2677 return array(); 2678 } 2679 2680 // Parse the formula and return the token stack 2681 return $this->_parseFormula($formula); 2682 } 2683 2684 2685 /** 2686 * Calculate the value of a formula 2687 * 2688 * @param string $formula Formula to parse 2689 * @param string $cellID Address of the cell to calculate 2690 * @param PHPExcel_Cell $pCell Cell to calculate 2691 * @return mixed 2692 * @throws PHPExcel_Calculation_Exception 2693 */ 2694 public function calculateFormula($formula, $cellID = null, PHPExcel_Cell $pCell = null) 2695 { 2696 // Initialise the logging settings 2697 $this->formulaError = null; 2698 $this->_debugLog->clearLog(); 2699 $this->cyclicReferenceStack->clear(); 2700 2701 if ($this->workbook !== null && $cellID === null && $pCell === null) { 2702 $cellID = 'A1'; 2703 $pCell = $this->workbook->getActiveSheet()->getCell($cellID); 2704 } else { 2705 // Disable calculation cacheing because it only applies to cell calculations, not straight formulae 2706 // But don't actually flush any cache 2707 $resetCache = $this->getCalculationCacheEnabled(); 2708 $this->calculationCacheEnabled = false; 2709 } 2710 2711 // Execute the calculation 2712 try { 2713 $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell)); 2714 } catch (PHPExcel_Exception $e) { 2715 throw new PHPExcel_Calculation_Exception($e->getMessage()); 2716 } 2717 2718 if ($this->workbook === null) { 2719 // Reset calculation cacheing to its previous state 2720 $this->calculationCacheEnabled = $resetCache; 2721 } 2722 2723 return $result; 2724 } 2725 2726 2727 public function getValueFromCache($cellReference, &$cellValue) 2728 { 2729 // Is calculation cacheing enabled? 2730 // Is the value present in calculation cache? 2731 $this->_debugLog->writeDebugLog('Testing cache value for cell ', $cellReference); 2732 if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) { 2733 $this->_debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache'); 2734 // Return the cached result 2735 $cellValue = $this->calculationCache[$cellReference]; 2736 return true; 2737 } 2738 return false; 2739 } 2740 2741 public function saveValueToCache($cellReference, $cellValue) 2742 { 2743 if ($this->calculationCacheEnabled) { 2744 $this->calculationCache[$cellReference] = $cellValue; 2745 } 2746 } 2747 2748 /** 2749 * Parse a cell formula and calculate its value 2750 * 2751 * @param string $formula The formula to parse and calculate 2752 * @param string $cellID The ID (e.g. A3) of the cell that we are calculating 2753 * @param PHPExcel_Cell $pCell Cell to calculate 2754 * @return mixed 2755 * @throws PHPExcel_Calculation_Exception 2756 */ 2757 public function _calculateFormulaValue($formula, $cellID = null, PHPExcel_Cell $pCell = null) 2758 { 2759 $cellValue = null; 2760 2761 // Basic validation that this is indeed a formula 2762 // We simply return the cell value if not 2763 $formula = trim($formula); 2764 if ($formula{0} != '=') { 2765 return self::wrapResult($formula); 2766 } 2767 $formula = ltrim(substr($formula, 1)); 2768 if (!isset($formula{0})) { 2769 return self::wrapResult($formula); 2770 } 2771 2772 $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null; 2773 $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk"; 2774 $wsCellReference = $wsTitle . '!' . $cellID; 2775 2776 if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) { 2777 return $cellValue; 2778 } 2779 2780 if (($wsTitle{0} !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) { 2781 if ($this->cyclicFormulaCount <= 0) { 2782 $this->cyclicFormulaCell = ''; 2783 return $this->raiseFormulaError('Cyclic Reference in Formula'); 2784 } elseif ($this->cyclicFormulaCell === $wsCellReference) { 2785 ++$this->cyclicFormulaCounter; 2786 if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) { 2787 $this->cyclicFormulaCell = ''; 2788 return $cellValue; 2789 } 2790 } elseif ($this->cyclicFormulaCell == '') { 2791 if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) { 2792 return $cellValue; 2793 } 2794 $this->cyclicFormulaCell = $wsCellReference; 2795 } 2796 } 2797 2798 // Parse the formula onto the token stack and calculate the value 2799 $this->cyclicReferenceStack->push($wsCellReference); 2800 $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell); 2801 $this->cyclicReferenceStack->pop(); 2802 2803 // Save to calculation cache 2804 if ($cellID !== null) { 2805 $this->saveValueToCache($wsCellReference, $cellValue); 2806 } 2807 2808 // Return the calculated value 2809 return $cellValue; 2810 } 2811 2812 2813 /** 2814 * Ensure that paired matrix operands are both matrices and of the same size 2815 * 2816 * @param mixed &$operand1 First matrix operand 2817 * @param mixed &$operand2 Second matrix operand 2818 * @param integer $resize Flag indicating whether the matrices should be resized to match 2819 * and (if so), whether the smaller dimension should grow or the 2820 * larger should shrink. 2821 * 0 = no resize 2822 * 1 = shrink to fit 2823 * 2 = extend to fit 2824 */ 2825 private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1) 2826 { 2827 // Examine each of the two operands, and turn them into an array if they aren't one already 2828 // Note that this function should only be called if one or both of the operand is already an array 2829 if (!is_array($operand1)) { 2830 list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2); 2831 $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1)); 2832 $resize = 0; 2833 } elseif (!is_array($operand2)) { 2834 list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1); 2835 $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2)); 2836 $resize = 0; 2837 } 2838 2839 list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1); 2840 list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2); 2841 if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) { 2842 $resize = 1; 2843 } 2844 2845 if ($resize == 2) { 2846 // Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger 2847 self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns); 2848 } elseif ($resize == 1) { 2849 // Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller 2850 self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns); 2851 } 2852 return array( $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns); 2853 } 2854 2855 2856 /** 2857 * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0 2858 * 2859 * @param mixed &$matrix matrix operand 2860 * @return array An array comprising the number of rows, and number of columns 2861 */ 2862 private static function getMatrixDimensions(&$matrix) 2863 { 2864 $matrixRows = count($matrix); 2865 $matrixColumns = 0; 2866 foreach ($matrix as $rowKey => $rowValue) { 2867 $matrixColumns = max(count($rowValue), $matrixColumns); 2868 if (!is_array($rowValue)) { 2869 $matrix[$rowKey] = array($rowValue); 2870 } else { 2871 $matrix[$rowKey] = array_values($rowValue); 2872 } 2873 } 2874 $matrix = array_values($matrix); 2875 return array($matrixRows, $matrixColumns); 2876 } 2877 2878 2879 /** 2880 * Ensure that paired matrix operands are both matrices of the same size 2881 * 2882 * @param mixed &$matrix1 First matrix operand 2883 * @param mixed &$matrix2 Second matrix operand 2884 * @param integer $matrix1Rows Row size of first matrix operand 2885 * @param integer $matrix1Columns Column size of first matrix operand 2886 * @param integer $matrix2Rows Row size of second matrix operand 2887 * @param integer $matrix2Columns Column size of second matrix operand 2888 */ 2889 private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns) 2890 { 2891 if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) { 2892 if ($matrix2Rows < $matrix1Rows) { 2893 for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) { 2894 unset($matrix1[$i]); 2895 } 2896 } 2897 if ($matrix2Columns < $matrix1Columns) { 2898 for ($i = 0; $i < $matrix1Rows; ++$i) { 2899 for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) { 2900 unset($matrix1[$i][$j]); 2901 } 2902 } 2903 } 2904 } 2905 2906 if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) { 2907 if ($matrix1Rows < $matrix2Rows) { 2908 for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) { 2909 unset($matrix2[$i]); 2910 } 2911 } 2912 if ($matrix1Columns < $matrix2Columns) { 2913 for ($i = 0; $i < $matrix2Rows; ++$i) { 2914 for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) { 2915 unset($matrix2[$i][$j]); 2916 } 2917 } 2918 } 2919 } 2920 } 2921 2922 2923 /** 2924 * Ensure that paired matrix operands are both matrices of the same size 2925 * 2926 * @param mixed &$matrix1 First matrix operand 2927 * @param mixed &$matrix2 Second matrix operand 2928 * @param integer $matrix1Rows Row size of first matrix operand 2929 * @param integer $matrix1Columns Column size of first matrix operand 2930 * @param integer $matrix2Rows Row size of second matrix operand 2931 * @param integer $matrix2Columns Column size of second matrix operand 2932 */ 2933 private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns) 2934 { 2935 if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) { 2936 if ($matrix2Columns < $matrix1Columns) { 2937 for ($i = 0; $i < $matrix2Rows; ++$i) { 2938 $x = $matrix2[$i][$matrix2Columns-1]; 2939 for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) { 2940 $matrix2[$i][$j] = $x; 2941 } 2942 } 2943 } 2944 if ($matrix2Rows < $matrix1Rows) { 2945 $x = $matrix2[$matrix2Rows-1]; 2946 for ($i = 0; $i < $matrix1Rows; ++$i) { 2947 $matrix2[$i] = $x; 2948 } 2949 } 2950 } 2951 2952 if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) { 2953 if ($matrix1Columns < $matrix2Columns) { 2954 for ($i = 0; $i < $matrix1Rows; ++$i) { 2955 $x = $matrix1[$i][$matrix1Columns-1]; 2956 for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) { 2957 $matrix1[$i][$j] = $x; 2958 } 2959 } 2960 } 2961 if ($matrix1Rows < $matrix2Rows) { 2962 $x = $matrix1[$matrix1Rows-1]; 2963 for ($i = 0; $i < $matrix2Rows; ++$i) { 2964 $matrix1[$i] = $x; 2965 } 2966 } 2967 } 2968 } 2969 2970 2971 /** 2972 * Format details of an operand for display in the log (based on operand type) 2973 * 2974 * @param mixed $value First matrix operand 2975 * @return mixed 2976 */ 2977 private function showValue($value) 2978 { 2979 if ($this->_debugLog->getWriteDebugLog()) { 2980 $testArray = PHPExcel_Calculation_Functions::flattenArray($value); 2981 if (count($testArray) == 1) { 2982 $value = array_pop($testArray); 2983 } 2984 2985 if (is_array($value)) { 2986 $returnMatrix = array(); 2987 $pad = $rpad = ', '; 2988 foreach ($value as $row) { 2989 if (is_array($row)) { 2990 $returnMatrix[] = implode($pad, array_map(array($this, 'showValue'), $row)); 2991 $rpad = '; '; 2992 } else { 2993 $returnMatrix[] = $this->showValue($row); 2994 } 2995 } 2996 return '{ '.implode($rpad, $returnMatrix).' }'; 2997 } elseif (is_string($value) && (trim($value, '"') == $value)) { 2998 return '"'.$value.'"'; 2999 } elseif (is_bool($value)) { 3000 return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; 3001 } 3002 } 3003 return PHPExcel_Calculation_Functions::flattenSingleValue($value); 3004 } 3005 3006 3007 /** 3008 * Format type and details of an operand for display in the log (based on operand type) 3009 * 3010 * @param mixed $value First matrix operand 3011 * @return mixed 3012 */ 3013 private function showTypeDetails($value) 3014 { 3015 if ($this->_debugLog->getWriteDebugLog()) { 3016 $testArray = PHPExcel_Calculation_Functions::flattenArray($value); 3017 if (count($testArray) == 1) { 3018 $value = array_pop($testArray); 3019 } 3020 3021 if ($value === null) { 3022 return 'a NULL value'; 3023 } elseif (is_float($value)) { 3024 $typeString = 'a floating point number'; 3025 } elseif (is_int($value)) { 3026 $typeString = 'an integer number'; 3027 } elseif (is_bool($value)) { 3028 $typeString = 'a boolean'; 3029 } elseif (is_array($value)) { 3030 $typeString = 'a matrix'; 3031 } else { 3032 if ($value == '') { 3033 return 'an empty string'; 3034 } elseif ($value{0} == '#') { 3035 return 'a '.$value.' error'; 3036 } else { 3037 $typeString = 'a string'; 3038 } 3039 } 3040 return $typeString.' with a value of '.$this->showValue($value); 3041 } 3042 } 3043 3044 3045 private function convertMatrixReferences($formula) 3046 { 3047 static $matrixReplaceFrom = array('{', ';', '}'); 3048 static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'); 3049 3050 // Convert any Excel matrix references to the MKMATRIX() function 3051 if (strpos($formula, '{') !== false) { 3052 // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators 3053 if (strpos($formula, '"') !== false) { 3054 // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded 3055 // the formula 3056 $temp = explode('"', $formula); 3057 // Open and Closed counts used for trapping mismatched braces in the formula 3058 $openCount = $closeCount = 0; 3059 $i = false; 3060 foreach ($temp as &$value) { 3061 // Only count/replace in alternating array entries 3062 if ($i = !$i) { 3063 $openCount += substr_count($value, '{'); 3064 $closeCount += substr_count($value, '}'); 3065 $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value); 3066 } 3067 } 3068 unset($value); 3069 // Then rebuild the formula string 3070 $formula = implode('"', $temp); 3071 } else { 3072 // If there's no quoted strings, then we do a simple count/replace 3073 $openCount = substr_count($formula, '{'); 3074 $closeCount = substr_count($formula, '}'); 3075 $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula); 3076 } 3077 // Trap for mismatched braces and trigger an appropriate error 3078 if ($openCount < $closeCount) { 3079 if ($openCount > 0) { 3080 return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'"); 3081 } else { 3082 return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered"); 3083 } 3084 } elseif ($openCount > $closeCount) { 3085 if ($closeCount > 0) { 3086 return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'"); 3087 } else { 3088 return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered"); 3089 } 3090 } 3091 } 3092 3093 return $formula; 3094 } 3095 3096 3097 private static function mkMatrix() 3098 { 3099 return func_get_args(); 3100 } 3101 3102 3103 // Binary Operators 3104 // These operators always work on two values 3105 // Array key is the operator, the value indicates whether this is a left or right associative operator 3106 private static $operatorAssociativity = array( 3107 '^' => 0, // Exponentiation 3108 '*' => 0, '/' => 0, // Multiplication and Division 3109 '+' => 0, '-' => 0, // Addition and Subtraction 3110 '&' => 0, // Concatenation 3111 '|' => 0, ':' => 0, // Intersect and Range 3112 '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison 3113 ); 3114 3115 // Comparison (Boolean) Operators 3116 // These operators work on two values, but always return a boolean result 3117 private static $comparisonOperators = array('>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true); 3118 3119 // Operator Precedence 3120 // This list includes all valid operators, whether binary (including boolean) or unary (such as %) 3121 // Array key is the operator, the value is its precedence 3122 private static $operatorPrecedence = array( 3123 ':' => 8, // Range 3124 '|' => 7, // Intersect 3125 '~' => 6, // Negation 3126 '%' => 5, // Percentage 3127 '^' => 4, // Exponentiation 3128 '*' => 3, '/' => 3, // Multiplication and Division 3129 '+' => 2, '-' => 2, // Addition and Subtraction 3130 '&' => 1, // Concatenation 3131 '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison 3132 ); 3133 3134 // Convert infix to postfix notation 3135 private function _parseFormula($formula, PHPExcel_Cell $pCell = null) 3136 { 3137 if (($formula = $this->convertMatrixReferences(trim($formula))) === false) { 3138 return false; 3139 } 3140 3141 // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet), 3142 // so we store the parent worksheet so that we can re-attach it when necessary 3143 $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null; 3144 3145 $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION. 3146 '|'.self::CALCULATION_REGEXP_CELLREF. 3147 '|'.self::CALCULATION_REGEXP_NUMBER. 3148 '|'.self::CALCULATION_REGEXP_STRING. 3149 '|'.self::CALCULATION_REGEXP_OPENBRACE. 3150 '|'.self::CALCULATION_REGEXP_NAMEDRANGE. 3151 '|'.self::CALCULATION_REGEXP_ERROR. 3152 ')/si'; 3153 3154 // Start with initialisation 3155 $index = 0; 3156 $stack = new PHPExcel_Calculation_Token_Stack; 3157 $output = array(); 3158 $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a 3159 // - is a negation or + is a positive operator rather than an operation 3160 $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand 3161 // should be null in a function call 3162 // The guts of the lexical parser 3163 // Loop through the formula extracting each operator and operand in turn 3164 while (true) { 3165//echo 'Assessing Expression '.substr($formula, $index), PHP_EOL; 3166 $opCharacter = $formula{$index}; // Get the first character of the value at the current index position 3167//echo 'Initial character of expression block is '.$opCharacter, PHP_EOL; 3168 if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula{$index+1}]))) { 3169 $opCharacter .= $formula{++$index}; 3170//echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL; 3171 } 3172 3173 // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand 3174 $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match); 3175//echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL; 3176//var_dump($match); 3177 3178 if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus? 3179//echo 'Element is a Negation operator', PHP_EOL; 3180 $stack->push('Unary Operator', '~'); // Put a negation on the stack 3181 ++$index; // and drop the negation symbol 3182 } elseif ($opCharacter == '%' && $expectingOperator) { 3183//echo 'Element is a Percentage operator', PHP_EOL; 3184 $stack->push('Unary Operator', '%'); // Put a percentage on the stack 3185 ++$index; 3186 } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded? 3187//echo 'Element is a Positive number, not Plus operator', PHP_EOL; 3188 ++$index; // Drop the redundant plus symbol 3189 } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal 3190 return $this->raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression 3191 3192 } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack? 3193//echo 'Element with value '.$opCharacter.' is an Operator', PHP_EOL; 3194 while ($stack->count() > 0 && 3195 ($o2 = $stack->last()) && 3196 isset(self::$operators[$o2['value']]) && 3197 @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) { 3198 $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output 3199 } 3200 $stack->push('Binary Operator', $opCharacter); // Finally put our current operator onto the stack 3201 ++$index; 3202 $expectingOperator = false; 3203 3204 } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis? 3205//echo 'Element is a Closing bracket', PHP_EOL; 3206 $expectingOperand = false; 3207 while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last ( 3208 if ($o2 === null) { 3209 return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"'); 3210 } else { 3211 $output[] = $o2; 3212 } 3213 } 3214 $d = $stack->last(2); 3215 if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) { // Did this parenthesis just close a function? 3216 $functionName = $matches[1]; // Get the function name 3217//echo 'Closed Function is '.$functionName, PHP_EOL; 3218 $d = $stack->pop(); 3219 $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack) 3220//if ($argumentCount == 0) { 3221// echo 'With no arguments', PHP_EOL; 3222//} elseif ($argumentCount == 1) { 3223// echo 'With 1 argument', PHP_EOL; 3224//} else { 3225// echo 'With '.$argumentCount.' arguments', PHP_EOL; 3226//} 3227 $output[] = $d; // Dump the argument count on the output 3228 $output[] = $stack->pop(); // Pop the function and push onto the output 3229 if (isset(self::$controlFunctions[$functionName])) { 3230//echo 'Built-in function '.$functionName, PHP_EOL; 3231 $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount']; 3232 $functionCall = self::$controlFunctions[$functionName]['functionCall']; 3233 } elseif (isset(self::$PHPExcelFunctions[$functionName])) { 3234//echo 'PHPExcel function '.$functionName, PHP_EOL; 3235 $expectedArgumentCount = self::$PHPExcelFunctions[$functionName]['argumentCount']; 3236 $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall']; 3237 } else { // did we somehow push a non-function on the stack? this should never happen 3238 return $this->raiseFormulaError("Formula Error: Internal error, non-function on stack"); 3239 } 3240 // Check the argument count 3241 $argumentCountError = false; 3242 if (is_numeric($expectedArgumentCount)) { 3243 if ($expectedArgumentCount < 0) { 3244//echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount), PHP_EOL; 3245 if ($argumentCount > abs($expectedArgumentCount)) { 3246 $argumentCountError = true; 3247 $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount); 3248 } 3249 } else { 3250//echo '$expectedArgumentCount is numeric '.$expectedArgumentCount, PHP_EOL; 3251 if ($argumentCount != $expectedArgumentCount) { 3252 $argumentCountError = true; 3253 $expectedArgumentCountString = $expectedArgumentCount; 3254 } 3255 } 3256 } elseif ($expectedArgumentCount != '*') { 3257 $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch); 3258//print_r($argMatch); 3259//echo PHP_EOL; 3260 switch ($argMatch[2]) { 3261 case '+': 3262 if ($argumentCount < $argMatch[1]) { 3263 $argumentCountError = true; 3264 $expectedArgumentCountString = $argMatch[1].' or more '; 3265 } 3266 break; 3267 case '-': 3268 if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) { 3269 $argumentCountError = true; 3270 $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3]; 3271 } 3272 break; 3273 case ',': 3274 if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) { 3275 $argumentCountError = true; 3276 $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3]; 3277 } 3278 break; 3279 } 3280 } 3281 if ($argumentCountError) { 3282 return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, ".$expectedArgumentCountString." expected"); 3283 } 3284 } 3285 ++$index; 3286 3287 } elseif ($opCharacter == ',') { // Is this the separator for function arguments? 3288//echo 'Element is a Function argument separator', PHP_EOL; 3289 while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last ( 3290 if ($o2 === null) { 3291 return $this->raiseFormulaError("Formula Error: Unexpected ,"); 3292 } else { 3293 $output[] = $o2; // pop the argument expression stuff and push onto the output 3294 } 3295 } 3296 // If we've a comma when we're expecting an operand, then what we actually have is a null operand; 3297 // so push a null onto the stack 3298 if (($expectingOperand) || (!$expectingOperator)) { 3299 $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null); 3300 } 3301 // make sure there was a function 3302 $d = $stack->last(2); 3303 if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) { 3304 return $this->raiseFormulaError("Formula Error: Unexpected ,"); 3305 } 3306 $d = $stack->pop(); 3307 $stack->push($d['type'], ++$d['value'], $d['reference']); // increment the argument count 3308 $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again 3309 $expectingOperator = false; 3310 $expectingOperand = true; 3311 ++$index; 3312 3313 } elseif ($opCharacter == '(' && !$expectingOperator) { 3314// echo 'Element is an Opening Bracket<br />'; 3315 $stack->push('Brace', '('); 3316 ++$index; 3317 3318 } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number? 3319 $expectingOperator = true; 3320 $expectingOperand = false; 3321 $val = $match[1]; 3322 $length = strlen($val); 3323// echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function<br />'; 3324 3325 if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) { 3326 $val = preg_replace('/\s/u', '', $val); 3327// echo 'Element '.$val.' is a Function<br />'; 3328 if (isset(self::$PHPExcelFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function 3329 $stack->push('Function', strtoupper($val)); 3330 $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index+$length), $amatch); 3331 if ($ax) { 3332 $stack->push('Operand Count for Function '.strtoupper($val).')', 0); 3333 $expectingOperator = true; 3334 } else { 3335 $stack->push('Operand Count for Function '.strtoupper($val).')', 1); 3336 $expectingOperator = false; 3337 } 3338 $stack->push('Brace', '('); 3339 } else { // it's a var w/ implicit multiplication 3340 $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => null); 3341 } 3342 } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) { 3343// echo 'Element '.$val.' is a Cell reference<br />'; 3344 // Watch for this case-change when modifying to allow cell references in different worksheets... 3345 // Should only be applied to the actual cell column, not the worksheet name 3346 3347 // If the last entry on the stack was a : operator, then we have a cell range reference 3348 $testPrevOp = $stack->last(1); 3349 if ($testPrevOp['value'] == ':') { 3350 // If we have a worksheet reference, then we're playing with a 3D reference 3351 if ($matches[2] == '') { 3352 // Otherwise, we 'inherit' the worksheet reference from the start cell reference 3353 // The start of the cell range reference should be the last entry in $output 3354 $startCellRef = $output[count($output)-1]['value']; 3355 preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches); 3356 if ($startMatches[2] > '') { 3357 $val = $startMatches[2].'!'.$val; 3358 } 3359 } else { 3360 return $this->raiseFormulaError("3D Range references are not yet supported"); 3361 } 3362 } 3363 3364 $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val); 3365// $expectingOperator = FALSE; 3366 } else { // it's a variable, constant, string, number or boolean 3367// echo 'Element is a Variable, Constant, String, Number or Boolean<br />'; 3368 // If the last entry on the stack was a : operator, then we may have a row or column range reference 3369 $testPrevOp = $stack->last(1); 3370 if ($testPrevOp['value'] == ':') { 3371 $startRowColRef = $output[count($output)-1]['value']; 3372 $rangeWS1 = ''; 3373 if (strpos('!', $startRowColRef) !== false) { 3374 list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef); 3375 } 3376 if ($rangeWS1 != '') { 3377 $rangeWS1 .= '!'; 3378 } 3379 $rangeWS2 = $rangeWS1; 3380 if (strpos('!', $val) !== false) { 3381 list($rangeWS2, $val) = explode('!', $val); 3382 } 3383 if ($rangeWS2 != '') { 3384 $rangeWS2 .= '!'; 3385 } 3386 if ((is_integer($startRowColRef)) && (ctype_digit($val)) && 3387 ($startRowColRef <= 1048576) && ($val <= 1048576)) { 3388 // Row range 3389 $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007 3390 $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef; 3391 $val = $rangeWS2.$endRowColRef.$val; 3392 } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) && 3393 (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) { 3394 // Column range 3395 $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007 3396 $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1'; 3397 $val = $rangeWS2.$val.$endRowColRef; 3398 } 3399 } 3400 3401 $localeConstant = false; 3402 if ($opCharacter == '"') { 3403// echo 'Element is a String<br />'; 3404 // UnEscape any quotes within the string 3405 $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val))); 3406 } elseif (is_numeric($val)) { 3407// echo 'Element is a Number<br />'; 3408 if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { 3409// echo 'Casting '.$val.' to float<br />'; 3410 $val = (float) $val; 3411 } else { 3412// echo 'Casting '.$val.' to integer<br />'; 3413 $val = (integer) $val; 3414 } 3415 } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) { 3416 $excelConstant = trim(strtoupper($val)); 3417// echo 'Element '.$excelConstant.' is an Excel Constant<br />'; 3418 $val = self::$excelConstants[$excelConstant]; 3419 } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) { 3420// echo 'Element '.$localeConstant.' is an Excel Constant<br />'; 3421 $val = self::$excelConstants[$localeConstant]; 3422 } 3423 $details = array('type' => 'Value', 'value' => $val, 'reference' => null); 3424 if ($localeConstant) { 3425 $details['localeValue'] = $localeConstant; 3426 } 3427 $output[] = $details; 3428 } 3429 $index += $length; 3430 3431 } elseif ($opCharacter == '$') { // absolute row or column range 3432 ++$index; 3433 } elseif ($opCharacter == ')') { // miscellaneous error checking 3434 if ($expectingOperand) { 3435 $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null); 3436 $expectingOperand = false; 3437 $expectingOperator = true; 3438 } else { 3439 return $this->raiseFormulaError("Formula Error: Unexpected ')'"); 3440 } 3441 } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) { 3442 return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'"); 3443 } else { // I don't even want to know what you did to get here 3444 return $this->raiseFormulaError("Formula Error: An unexpected error occured"); 3445 } 3446 // Test for end of formula string 3447 if ($index == strlen($formula)) { 3448 // Did we end with an operator?. 3449 // Only valid for the % unary operator 3450 if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) { 3451 return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands"); 3452 } else { 3453 break; 3454 } 3455 } 3456 // Ignore white space 3457 while (($formula{$index} == "\n") || ($formula{$index} == "\r")) { 3458 ++$index; 3459 } 3460 if ($formula{$index} == ' ') { 3461 while ($formula{$index} == ' ') { 3462 ++$index; 3463 } 3464 // If we're expecting an operator, but only have a space between the previous and next operands (and both are 3465 // Cell References) then we have an INTERSECTION operator 3466// echo 'Possible Intersect Operator<br />'; 3467 if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) && 3468 ($output[count($output)-1]['type'] == 'Cell Reference')) { 3469// echo 'Element is an Intersect Operator<br />'; 3470 while ($stack->count() > 0 && 3471 ($o2 = $stack->last()) && 3472 isset(self::$operators[$o2['value']]) && 3473 @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) { 3474 $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output 3475 } 3476 $stack->push('Binary Operator', '|'); // Put an Intersect Operator on the stack 3477 $expectingOperator = false; 3478 } 3479 } 3480 } 3481 3482 while (($op = $stack->pop()) !== null) { // pop everything off the stack and push onto output 3483 if ((is_array($op) && $op['value'] == '(') || ($op === '(')) { 3484 return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced 3485 } 3486 $output[] = $op; 3487 } 3488 return $output; 3489 } 3490 3491 3492 private static function dataTestReference(&$operandData) 3493 { 3494 $operand = $operandData['value']; 3495 if (($operandData['reference'] === null) && (is_array($operand))) { 3496 $rKeys = array_keys($operand); 3497 $rowKey = array_shift($rKeys); 3498 $cKeys = array_keys(array_keys($operand[$rowKey])); 3499 $colKey = array_shift($cKeys); 3500 if (ctype_upper($colKey)) { 3501 $operandData['reference'] = $colKey.$rowKey; 3502 } 3503 } 3504 return $operand; 3505 } 3506 3507 // evaluate postfix notation 3508 private function processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null) 3509 { 3510 if ($tokens == false) { 3511 return false; 3512 } 3513 3514 // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection), 3515 // so we store the parent cell collection so that we can re-attach it when necessary 3516 $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null; 3517 $pCellParent = ($pCell !== null) ? $pCell->getParent() : null; 3518 $stack = new PHPExcel_Calculation_Token_Stack; 3519 3520 // Loop through each token in turn 3521 foreach ($tokens as $tokenData) { 3522// print_r($tokenData); 3523// echo '<br />'; 3524 $token = $tokenData['value']; 3525// echo '<b>Token is '.$token.'</b><br />'; 3526 // 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 3527 if (isset(self::$binaryOperators[$token])) { 3528// echo 'Token is a binary operator<br />'; 3529 // We must have two operands, error if we don't 3530 if (($operand2Data = $stack->pop()) === null) { 3531 return $this->raiseFormulaError('Internal error - Operand value missing from stack'); 3532 } 3533 if (($operand1Data = $stack->pop()) === null) { 3534 return $this->raiseFormulaError('Internal error - Operand value missing from stack'); 3535 } 3536 3537 $operand1 = self::dataTestReference($operand1Data); 3538 $operand2 = self::dataTestReference($operand2Data); 3539 3540 // Log what we're doing 3541 if ($token == ':') { 3542 $this->_debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference'])); 3543 } else { 3544 $this->_debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2)); 3545 } 3546 3547 // Process the operation in the appropriate manner 3548 switch ($token) { 3549 // Comparison (Boolean) Operators 3550 case '>': // Greater than 3551 case '<': // Less than 3552 case '>=': // Greater than or Equal to 3553 case '<=': // Less than or Equal to 3554 case '=': // Equality 3555 case '<>': // Inequality 3556 $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack); 3557 break; 3558 // Binary Operators 3559 case ':': // Range 3560 $sheet1 = $sheet2 = ''; 3561 if (strpos($operand1Data['reference'], '!') !== false) { 3562 list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']); 3563 } else { 3564 $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : ''; 3565 } 3566 if (strpos($operand2Data['reference'], '!') !== false) { 3567 list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']); 3568 } else { 3569 $sheet2 = $sheet1; 3570 } 3571 if ($sheet1 == $sheet2) { 3572 if ($operand1Data['reference'] === null) { 3573 if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) { 3574 $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value']; 3575 } elseif (trim($operand1Data['reference']) == '') { 3576 $operand1Data['reference'] = $pCell->getCoordinate(); 3577 } else { 3578 $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow(); 3579 } 3580 } 3581 if ($operand2Data['reference'] === null) { 3582 if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) { 3583 $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value']; 3584 } elseif (trim($operand2Data['reference']) == '') { 3585 $operand2Data['reference'] = $pCell->getCoordinate(); 3586 } else { 3587 $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow(); 3588 } 3589 } 3590 3591 $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference'])); 3592 $oCol = $oRow = array(); 3593 foreach ($oData as $oDatum) { 3594 $oCR = PHPExcel_Cell::coordinateFromString($oDatum); 3595 $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1; 3596 $oRow[] = $oCR[1]; 3597 } 3598 $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); 3599 if ($pCellParent !== null) { 3600 $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($sheet1), false); 3601 } else { 3602 return $this->raiseFormulaError('Unable to access Cell Reference'); 3603 } 3604 $stack->push('Cell Reference', $cellValue, $cellRef); 3605 } else { 3606 $stack->push('Error', PHPExcel_Calculation_Functions::REF(), null); 3607 } 3608 break; 3609 case '+': // Addition 3610 $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack); 3611 break; 3612 case '-': // Subtraction 3613 $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack); 3614 break; 3615 case '*': // Multiplication 3616 $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack); 3617 break; 3618 case '/': // Division 3619 $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack); 3620 break; 3621 case '^': // Exponential 3622 $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack); 3623 break; 3624 case '&': // Concatenation 3625 // If either of the operands is a matrix, we need to treat them both as matrices 3626 // (converting the other operand to a matrix if need be); then perform the required 3627 // matrix operation 3628 if (is_bool($operand1)) { 3629 $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; 3630 } 3631 if (is_bool($operand2)) { 3632 $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; 3633 } 3634 if ((is_array($operand1)) || (is_array($operand2))) { 3635 // Ensure that both operands are arrays/matrices 3636 self::checkMatrixOperands($operand1, $operand2, 2); 3637 try { 3638 // Convert operand 1 from a PHP array to a matrix 3639 $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1); 3640 // Perform the required operation against the operand 1 matrix, passing in operand 2 3641 $matrixResult = $matrix->concat($operand2); 3642 $result = $matrixResult->getArray(); 3643 } catch (PHPExcel_Exception $ex) { 3644 $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); 3645 $result = '#VALUE!'; 3646 } 3647 } else { 3648 $result = '"'.str_replace('""', '"', self::unwrapResult($operand1, '"').self::unwrapResult($operand2, '"')).'"'; 3649 } 3650 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); 3651 $stack->push('Value', $result); 3652 break; 3653 case '|': // Intersect 3654 $rowIntersect = array_intersect_key($operand1, $operand2); 3655 $cellIntersect = $oCol = $oRow = array(); 3656 foreach (array_keys($rowIntersect) as $row) { 3657 $oRow[] = $row; 3658 foreach ($rowIntersect[$row] as $col => $data) { 3659 $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1; 3660 $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]); 3661 } 3662 } 3663 $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); 3664 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect)); 3665 $stack->push('Value', $cellIntersect, $cellRef); 3666 break; 3667 } 3668 3669 // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on 3670 } elseif (($token === '~') || ($token === '%')) { 3671// echo 'Token is a unary operator<br />'; 3672 if (($arg = $stack->pop()) === null) { 3673 return $this->raiseFormulaError('Internal error - Operand value missing from stack'); 3674 } 3675 $arg = $arg['value']; 3676 if ($token === '~') { 3677// echo 'Token is a negation operator<br />'; 3678 $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg)); 3679 $multiplier = -1; 3680 } else { 3681// echo 'Token is a percentile operator<br />'; 3682 $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg)); 3683 $multiplier = 0.01; 3684 } 3685 if (is_array($arg)) { 3686 self::checkMatrixOperands($arg, $multiplier, 2); 3687 try { 3688 $matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg); 3689 $matrixResult = $matrix1->arrayTimesEquals($multiplier); 3690 $result = $matrixResult->getArray(); 3691 } catch (PHPExcel_Exception $ex) { 3692 $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); 3693 $result = '#VALUE!'; 3694 } 3695 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); 3696 $stack->push('Value', $result); 3697 } else { 3698 $this->executeNumericBinaryOperation($cellID, $multiplier, $arg, '*', 'arrayTimesEquals', $stack); 3699 } 3700 3701 } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) { 3702 $cellRef = null; 3703// echo 'Element '.$token.' is a Cell reference<br />'; 3704 if (isset($matches[8])) { 3705// echo 'Reference is a Range of cells<br />'; 3706 if ($pCell === null) { 3707// We can't access the range, so return a REF error 3708 $cellValue = PHPExcel_Calculation_Functions::REF(); 3709 } else { 3710 $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10]; 3711 if ($matches[2] > '') { 3712 $matches[2] = trim($matches[2], "\"'"); 3713 if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) { 3714 // It's a Reference to an external workbook (not currently supported) 3715 return $this->raiseFormulaError('Unable to access External Workbook'); 3716 } 3717 $matches[2] = trim($matches[2], "\"'"); 3718// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; 3719 $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]); 3720 if ($pCellParent !== null) { 3721 $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false); 3722 } else { 3723 return $this->raiseFormulaError('Unable to access Cell Reference'); 3724 } 3725 $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue)); 3726// $cellRef = $matches[2].'!'.$cellRef; 3727 } else { 3728// echo '$cellRef='.$cellRef.' in current worksheet<br />'; 3729 $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet'); 3730 if ($pCellParent !== null) { 3731 $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); 3732 } else { 3733 return $this->raiseFormulaError('Unable to access Cell Reference'); 3734 } 3735 $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue)); 3736 } 3737 } 3738 } else { 3739// echo 'Reference is a single Cell<br />'; 3740 if ($pCell === null) { 3741// We can't access the cell, so return a REF error 3742 $cellValue = PHPExcel_Calculation_Functions::REF(); 3743 } else { 3744 $cellRef = $matches[6].$matches[7]; 3745 if ($matches[2] > '') { 3746 $matches[2] = trim($matches[2], "\"'"); 3747 if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) { 3748 // It's a Reference to an external workbook (not currently supported) 3749 return $this->raiseFormulaError('Unable to access External Workbook'); 3750 } 3751// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; 3752 $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]); 3753 if ($pCellParent !== null) { 3754 $cellSheet = $this->workbook->getSheetByName($matches[2]); 3755 if ($cellSheet && $cellSheet->cellExists($cellRef)) { 3756 $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false); 3757 $pCell->attach($pCellParent); 3758 } else { 3759 $cellValue = null; 3760 } 3761 } else { 3762 return $this->raiseFormulaError('Unable to access Cell Reference'); 3763 } 3764 $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue)); 3765// $cellRef = $matches[2].'!'.$cellRef; 3766 } else { 3767// echo '$cellRef='.$cellRef.' in current worksheet<br />'; 3768 $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet'); 3769 if ($pCellParent->isDataSet($cellRef)) { 3770 $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); 3771 $pCell->attach($pCellParent); 3772 } else { 3773 $cellValue = null; 3774 } 3775 $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue)); 3776 } 3777 } 3778 } 3779 $stack->push('Value', $cellValue, $cellRef); 3780 3781 // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on 3782 } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) { 3783// echo 'Token is a function<br />'; 3784 $functionName = $matches[1]; 3785 $argCount = $stack->pop(); 3786 $argCount = $argCount['value']; 3787 if ($functionName != 'MKMATRIX') { 3788 $this->_debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's')); 3789 } 3790 if ((isset(self::$PHPExcelFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function 3791 if (isset(self::$PHPExcelFunctions[$functionName])) { 3792 $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall']; 3793 $passByReference = isset(self::$PHPExcelFunctions[$functionName]['passByReference']); 3794 $passCellReference = isset(self::$PHPExcelFunctions[$functionName]['passCellReference']); 3795 } elseif (isset(self::$controlFunctions[$functionName])) { 3796 $functionCall = self::$controlFunctions[$functionName]['functionCall']; 3797 $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']); 3798 $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']); 3799 } 3800 // get the arguments for this function 3801// echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />'; 3802 $args = $argArrayVals = array(); 3803 for ($i = 0; $i < $argCount; ++$i) { 3804 $arg = $stack->pop(); 3805 $a = $argCount - $i - 1; 3806 if (($passByReference) && 3807 (isset(self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) && 3808 (self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) { 3809 if ($arg['reference'] === null) { 3810 $args[] = $cellID; 3811 if ($functionName != 'MKMATRIX') { 3812 $argArrayVals[] = $this->showValue($cellID); 3813 } 3814 } else { 3815 $args[] = $arg['reference']; 3816 if ($functionName != 'MKMATRIX') { 3817 $argArrayVals[] = $this->showValue($arg['reference']); 3818 } 3819 } 3820 } else { 3821 $args[] = self::unwrapResult($arg['value']); 3822 if ($functionName != 'MKMATRIX') { 3823 $argArrayVals[] = $this->showValue($arg['value']); 3824 } 3825 } 3826 } 3827 // Reverse the order of the arguments 3828 krsort($args); 3829 if (($passByReference) && ($argCount == 0)) { 3830 $args[] = $cellID; 3831 $argArrayVals[] = $this->showValue($cellID); 3832 } 3833// echo 'Arguments are: '; 3834// print_r($args); 3835// echo '<br />'; 3836 if ($functionName != 'MKMATRIX') { 3837 if ($this->_debugLog->getWriteDebugLog()) { 3838 krsort($argArrayVals); 3839 $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator.' ', PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )'); 3840 } 3841 } 3842 // Process each argument in turn, building the return value as an array 3843// if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) { 3844// $operand1 = $args[1]; 3845// $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->showValue($operand1)); 3846// $result = array(); 3847// $row = 0; 3848// foreach($operand1 as $args) { 3849// if (is_array($args)) { 3850// foreach($args as $arg) { 3851// $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($arg), ' )'); 3852// $r = call_user_func_array($functionCall, $arg); 3853// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r)); 3854// $result[$row][] = $r; 3855// } 3856// ++$row; 3857// } else { 3858// $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($args), ' )'); 3859// $r = call_user_func_array($functionCall, $args); 3860// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r)); 3861// $result[] = $r; 3862// } 3863// } 3864// } else { 3865 // Process the argument with the appropriate function call 3866 if ($passCellReference) { 3867 $args[] = $pCell; 3868 } 3869 if (strpos($functionCall, '::') !== false) { 3870 $result = call_user_func_array(explode('::', $functionCall), $args); 3871 } else { 3872 foreach ($args as &$arg) { 3873 $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg); 3874 } 3875 unset($arg); 3876 $result = call_user_func_array($functionCall, $args); 3877 } 3878 if ($functionName != 'MKMATRIX') { 3879 $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result)); 3880 } 3881 $stack->push('Value', self::wrapResult($result)); 3882 } 3883 3884 } else { 3885 // if the token is a number, boolean, string or an Excel error, push it onto the stack 3886 if (isset(self::$excelConstants[strtoupper($token)])) { 3887 $excelConstant = strtoupper($token); 3888// echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />'; 3889 $stack->push('Constant Value', self::$excelConstants[$excelConstant]); 3890 $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant])); 3891 } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) { 3892// echo 'Token is a number, boolean, string, null or an Excel error<br />'; 3893 $stack->push('Value', $token); 3894 // if the token is a named range, push the named range name onto the stack 3895 } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) { 3896// echo 'Token is a named range<br />'; 3897 $namedRange = $matches[6]; 3898// echo 'Named Range is '.$namedRange.'<br />'; 3899 $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange); 3900 $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false); 3901 $pCell->attach($pCellParent); 3902 $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue)); 3903 $stack->push('Named Range', $cellValue, $namedRange); 3904 } else { 3905 return $this->raiseFormulaError("undefined variable '$token'"); 3906 } 3907 } 3908 } 3909 // when we're out of tokens, the stack should have a single element, the final result 3910 if ($stack->count() != 1) { 3911 return $this->raiseFormulaError("internal error"); 3912 } 3913 $output = $stack->pop(); 3914 $output = $output['value']; 3915 3916// if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) { 3917// return array_shift(PHPExcel_Calculation_Functions::flattenArray($output)); 3918// } 3919 return $output; 3920 } 3921 3922 3923 private function validateBinaryOperand($cellID, &$operand, &$stack) 3924 { 3925 if (is_array($operand)) { 3926 if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) { 3927 do { 3928 $operand = array_pop($operand); 3929 } while (is_array($operand)); 3930 } 3931 } 3932 // Numbers, matrices and booleans can pass straight through, as they're already valid 3933 if (is_string($operand)) { 3934 // We only need special validations for the operand if it is a string 3935 // Start by stripping off the quotation marks we use to identify true excel string values internally 3936 if ($operand > '' && $operand{0} == '"') { 3937 $operand = self::unwrapResult($operand); 3938 } 3939 // If the string is a numeric value, we treat it as a numeric, so no further testing 3940 if (!is_numeric($operand)) { 3941 // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations 3942 if ($operand > '' && $operand{0} == '#') { 3943 $stack->push('Value', $operand); 3944 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand)); 3945 return false; 3946 } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) { 3947 // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations 3948 $stack->push('Value', '#VALUE!'); 3949 $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!')); 3950 return false; 3951 } 3952 } 3953 } 3954 3955 // return a true if the value of the operand is one that we can use in normal binary operations 3956 return true; 3957 } 3958 3959 3960 private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays = false) 3961 { 3962 // If we're dealing with matrix operations, we want a matrix result 3963 if ((is_array($operand1)) || (is_array($operand2))) { 3964 $result = array(); 3965 if ((is_array($operand1)) && (!is_array($operand2))) { 3966 foreach ($operand1 as $x => $operandData) { 3967 $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2)); 3968 $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack); 3969 $r = $stack->pop(); 3970 $result[$x] = $r['value']; 3971 } 3972 } elseif ((!is_array($operand1)) && (is_array($operand2))) { 3973 foreach ($operand2 as $x => $operandData) { 3974 $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData)); 3975 $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack); 3976 $r = $stack->pop(); 3977 $result[$x] = $r['value']; 3978 } 3979 } else { 3980 if (!$recursingArrays) { 3981 self::checkMatrixOperands($operand1, $operand2, 2); 3982 } 3983 foreach ($operand1 as $x => $operandData) { 3984 $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x])); 3985 $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true); 3986 $r = $stack->pop(); 3987 $result[$x] = $r['value']; 3988 } 3989 } 3990 // Log the result details 3991 $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result)); 3992 // And push the result onto the stack 3993 $stack->push('Array', $result); 3994 return true; 3995 } 3996 3997 // Simple validate the two operands if they are string values 3998 if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') { 3999 $operand1 = self::unwrapResult($operand1); 4000 } 4001 if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') { 4002 $operand2 = self::unwrapResult($operand2); 4003 } 4004 4005 // Use case insensitive comparaison if not OpenOffice mode 4006 if (PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) { 4007 if (is_string($operand1)) { 4008 $operand1 = strtoupper($operand1); 4009 } 4010 if (is_string($operand2)) { 4011 $operand2 = strtoupper($operand2); 4012 } 4013 } 4014 4015 $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE; 4016 4017 // execute the necessary operation 4018 switch ($operation) { 4019 // Greater than 4020 case '>': 4021 if ($useLowercaseFirstComparison) { 4022 $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0; 4023 } else { 4024 $result = ($operand1 > $operand2); 4025 } 4026 break; 4027 // Less than 4028 case '<': 4029 if ($useLowercaseFirstComparison) { 4030 $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0; 4031 } else { 4032 $result = ($operand1 < $operand2); 4033 } 4034 break; 4035 // Equality 4036 case '=': 4037 if (is_numeric($operand1) && is_numeric($operand2)) { 4038 $result = (abs($operand1 - $operand2) < $this->delta); 4039 } else { 4040 $result = strcmp($operand1, $operand2) == 0; 4041 } 4042 break; 4043 // Greater than or equal 4044 case '>=': 4045 if (is_numeric($operand1) && is_numeric($operand2)) { 4046 $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2)); 4047 } elseif ($useLowercaseFirstComparison) { 4048 $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0; 4049 } else { 4050 $result = strcmp($operand1, $operand2) >= 0; 4051 } 4052 break; 4053 // Less than or equal 4054 case '<=': 4055 if (is_numeric($operand1) && is_numeric($operand2)) { 4056 $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2)); 4057 } elseif ($useLowercaseFirstComparison) { 4058 $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0; 4059 } else { 4060 $result = strcmp($operand1, $operand2) <= 0; 4061 } 4062 break; 4063 // Inequality 4064 case '<>': 4065 if (is_numeric($operand1) && is_numeric($operand2)) { 4066 $result = (abs($operand1 - $operand2) > 1E-14); 4067 } else { 4068 $result = strcmp($operand1, $operand2) != 0; 4069 } 4070 break; 4071 } 4072 4073 // Log the result details 4074 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); 4075 // And push the result onto the stack 4076 $stack->push('Value', $result); 4077 return true; 4078 } 4079 4080 /** 4081 * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters 4082 * @param string $str1 First string value for the comparison 4083 * @param string $str2 Second string value for the comparison 4084 * @return integer 4085 */ 4086 private function strcmpLowercaseFirst($str1, $str2) 4087 { 4088 $inversedStr1 = PHPExcel_Shared_String::StrCaseReverse($str1); 4089 $inversedStr2 = PHPExcel_Shared_String::StrCaseReverse($str2); 4090 4091 return strcmp($inversedStr1, $inversedStr2); 4092 } 4093 4094 private function executeNumericBinaryOperation($cellID, $operand1, $operand2, $operation, $matrixFunction, &$stack) 4095 { 4096 // Validate the two operands 4097 if (!$this->validateBinaryOperand($cellID, $operand1, $stack)) { 4098 return false; 4099 } 4100 if (!$this->validateBinaryOperand($cellID, $operand2, $stack)) { 4101 return false; 4102 } 4103 4104 // If either of the operands is a matrix, we need to treat them both as matrices 4105 // (converting the other operand to a matrix if need be); then perform the required 4106 // matrix operation 4107 if ((is_array($operand1)) || (is_array($operand2))) { 4108 // Ensure that both operands are arrays/matrices of the same size 4109 self::checkMatrixOperands($operand1, $operand2, 2); 4110 4111 try { 4112 // Convert operand 1 from a PHP array to a matrix 4113 $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1); 4114 // Perform the required operation against the operand 1 matrix, passing in operand 2 4115 $matrixResult = $matrix->$matrixFunction($operand2); 4116 $result = $matrixResult->getArray(); 4117 } catch (PHPExcel_Exception $ex) { 4118 $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); 4119 $result = '#VALUE!'; 4120 } 4121 } else { 4122 if ((PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) && 4123 ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1)>0) || 4124 (is_string($operand2) && !is_numeric($operand2) && strlen($operand2)>0))) { 4125 $result = PHPExcel_Calculation_Functions::VALUE(); 4126 } else { 4127 // If we're dealing with non-matrix operations, execute the necessary operation 4128 switch ($operation) { 4129 // Addition 4130 case '+': 4131 $result = $operand1 + $operand2; 4132 break; 4133 // Subtraction 4134 case '-': 4135 $result = $operand1 - $operand2; 4136 break; 4137 // Multiplication 4138 case '*': 4139 $result = $operand1 * $operand2; 4140 break; 4141 // Division 4142 case '/': 4143 if ($operand2 == 0) { 4144 // Trap for Divide by Zero error 4145 $stack->push('Value', '#DIV/0!'); 4146 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!')); 4147 return false; 4148 } else { 4149 $result = $operand1 / $operand2; 4150 } 4151 break; 4152 // Power 4153 case '^': 4154 $result = pow($operand1, $operand2); 4155 break; 4156 } 4157 } 4158 } 4159 4160 // Log the result details 4161 $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); 4162 // And push the result onto the stack 4163 $stack->push('Value', $result); 4164 return true; 4165 } 4166 4167 4168 // trigger an error, but nicely, if need be 4169 protected function raiseFormulaError($errorMessage) 4170 { 4171 $this->formulaError = $errorMessage; 4172 $this->cyclicReferenceStack->clear(); 4173 if (!$this->suppressFormulaErrors) { 4174 throw new PHPExcel_Calculation_Exception($errorMessage); 4175 } 4176 trigger_error($errorMessage, E_USER_ERROR); 4177 } 4178 4179 4180 /** 4181 * Extract range values 4182 * 4183 * @param string &$pRange String based range representation 4184 * @param PHPExcel_Worksheet $pSheet Worksheet 4185 * @param boolean $resetLog Flag indicating whether calculation log should be reset or not 4186 * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. 4187 * @throws PHPExcel_Calculation_Exception 4188 */ 4189 public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true) 4190 { 4191 // Return value 4192 $returnValue = array (); 4193 4194// echo 'extractCellRange('.$pRange.')', PHP_EOL; 4195 if ($pSheet !== null) { 4196 $pSheetName = $pSheet->getTitle(); 4197// echo 'Passed sheet name is '.$pSheetName.PHP_EOL; 4198// echo 'Range reference is '.$pRange.PHP_EOL; 4199 if (strpos($pRange, '!') !== false) { 4200// echo '$pRange reference includes sheet reference', PHP_EOL; 4201 list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true); 4202// echo 'New sheet name is '.$pSheetName, PHP_EOL; 4203// echo 'Adjusted Range reference is '.$pRange, PHP_EOL; 4204 $pSheet = $this->workbook->getSheetByName($pSheetName); 4205 } 4206 4207 // Extract range 4208 $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange); 4209 $pRange = $pSheetName.'!'.$pRange; 4210 if (!isset($aReferences[1])) { 4211 // Single cell in range 4212 sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow); 4213 $cellValue = null; 4214 if ($pSheet->cellExists($aReferences[0])) { 4215 $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); 4216 } else { 4217 $returnValue[$currentRow][$currentCol] = null; 4218 } 4219 } else { 4220 // Extract cell data for all cells in the range 4221 foreach ($aReferences as $reference) { 4222 // Extract range 4223 sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow); 4224 $cellValue = null; 4225 if ($pSheet->cellExists($reference)) { 4226 $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); 4227 } else { 4228 $returnValue[$currentRow][$currentCol] = null; 4229 } 4230 } 4231 } 4232 } 4233 4234 return $returnValue; 4235 } 4236 4237 4238 /** 4239 * Extract range values 4240 * 4241 * @param string &$pRange String based range representation 4242 * @param PHPExcel_Worksheet $pSheet Worksheet 4243 * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. 4244 * @param boolean $resetLog Flag indicating whether calculation log should be reset or not 4245 * @throws PHPExcel_Calculation_Exception 4246 */ 4247 public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true) 4248 { 4249 // Return value 4250 $returnValue = array (); 4251 4252// echo 'extractNamedRange('.$pRange.')<br />'; 4253 if ($pSheet !== null) { 4254 $pSheetName = $pSheet->getTitle(); 4255// echo 'Current sheet name is '.$pSheetName.'<br />'; 4256// echo 'Range reference is '.$pRange.'<br />'; 4257 if (strpos($pRange, '!') !== false) { 4258// echo '$pRange reference includes sheet reference', PHP_EOL; 4259 list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true); 4260// echo 'New sheet name is '.$pSheetName, PHP_EOL; 4261// echo 'Adjusted Range reference is '.$pRange, PHP_EOL; 4262 $pSheet = $this->workbook->getSheetByName($pSheetName); 4263 } 4264 4265 // Named range? 4266 $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet); 4267 if ($namedRange !== null) { 4268 $pSheet = $namedRange->getWorksheet(); 4269// echo 'Named Range '.$pRange.' ('; 4270 $pRange = $namedRange->getRange(); 4271 $splitRange = PHPExcel_Cell::splitRange($pRange); 4272 // Convert row and column references 4273 if (ctype_alpha($splitRange[0][0])) { 4274 $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow(); 4275 } elseif (ctype_digit($splitRange[0][0])) { 4276 $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1]; 4277 } 4278// echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'<br />'; 4279 4280// if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) { 4281// if (!$namedRange->getLocalOnly()) { 4282// $pSheet = $namedRange->getWorksheet(); 4283// } else { 4284// return $returnValue; 4285// } 4286// } 4287 } else { 4288 return PHPExcel_Calculation_Functions::REF(); 4289 } 4290 4291 // Extract range 4292 $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange); 4293// var_dump($aReferences); 4294 if (!isset($aReferences[1])) { 4295 // Single cell (or single column or row) in range 4296 list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]); 4297 $cellValue = null; 4298 if ($pSheet->cellExists($aReferences[0])) { 4299 $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); 4300 } else { 4301 $returnValue[$currentRow][$currentCol] = null; 4302 } 4303 } else { 4304 // Extract cell data for all cells in the range 4305 foreach ($aReferences as $reference) { 4306 // Extract range 4307 list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($reference); 4308// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'<br />'; 4309 $cellValue = null; 4310 if ($pSheet->cellExists($reference)) { 4311 $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); 4312 } else { 4313 $returnValue[$currentRow][$currentCol] = null; 4314 } 4315 } 4316 } 4317// print_r($returnValue); 4318// echo '<br />'; 4319 } 4320 4321 return $returnValue; 4322 } 4323 4324 4325 /** 4326 * Is a specific function implemented? 4327 * 4328 * @param string $pFunction Function Name 4329 * @return boolean 4330 */ 4331 public function isImplemented($pFunction = '') 4332 { 4333 $pFunction = strtoupper($pFunction); 4334 if (isset(self::$PHPExcelFunctions[$pFunction])) { 4335 return (self::$PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY'); 4336 } else { 4337 return false; 4338 } 4339 } 4340 4341 4342 /** 4343 * Get a list of all implemented functions as an array of function objects 4344 * 4345 * @return array of PHPExcel_Calculation_Function 4346 */ 4347 public function listFunctions() 4348 { 4349 $returnValue = array(); 4350 4351 foreach (self::$PHPExcelFunctions as $functionName => $function) { 4352 if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') { 4353 $returnValue[$functionName] = new PHPExcel_Calculation_Function( 4354 $function['category'], 4355 $functionName, 4356 $function['functionCall'] 4357 ); 4358 } 4359 } 4360 4361 return $returnValue; 4362 } 4363 4364 4365 /** 4366 * Get a list of all Excel function names 4367 * 4368 * @return array 4369 */ 4370 public function listAllFunctionNames() 4371 { 4372 return array_keys(self::$PHPExcelFunctions); 4373 } 4374 4375 /** 4376 * Get a list of implemented Excel function names 4377 * 4378 * @return array 4379 */ 4380 public function listFunctionNames() 4381 { 4382 $returnValue = array(); 4383 foreach (self::$PHPExcelFunctions as $functionName => $function) { 4384 if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') { 4385 $returnValue[] = $functionName; 4386 } 4387 } 4388 4389 return $returnValue; 4390 } 4391} 4392