For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4391 lines
197 KiB

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