Functions.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Calculation;
  3. use PhpOffice\PhpSpreadsheet\Cell\Cell;
  4. class Functions
  5. {
  6. const PRECISION = 8.88E-016;
  7. /**
  8. * 2 / PI.
  9. */
  10. const M_2DIVPI = 0.63661977236758134307553505349006;
  11. /** constants */
  12. const COMPATIBILITY_EXCEL = 'Excel';
  13. const COMPATIBILITY_GNUMERIC = 'Gnumeric';
  14. const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
  15. const RETURNDATE_PHP_NUMERIC = 'P';
  16. const RETURNDATE_PHP_OBJECT = 'O';
  17. const RETURNDATE_EXCEL = 'E';
  18. /**
  19. * Compatibility mode to use for error checking and responses.
  20. *
  21. * @var string
  22. */
  23. protected static $compatibilityMode = self::COMPATIBILITY_EXCEL;
  24. /**
  25. * Data Type to use when returning date values.
  26. *
  27. * @var string
  28. */
  29. protected static $returnDateType = self::RETURNDATE_EXCEL;
  30. /**
  31. * List of error codes.
  32. *
  33. * @var array
  34. */
  35. protected static $errorCodes = [
  36. 'null' => '#NULL!',
  37. 'divisionbyzero' => '#DIV/0!',
  38. 'value' => '#VALUE!',
  39. 'reference' => '#REF!',
  40. 'name' => '#NAME?',
  41. 'num' => '#NUM!',
  42. 'na' => '#N/A',
  43. 'gettingdata' => '#GETTING_DATA',
  44. ];
  45. /**
  46. * Set the Compatibility Mode.
  47. *
  48. * @category Function Configuration
  49. *
  50. * @param string $compatibilityMode Compatibility Mode
  51. * Permitted values are:
  52. * Functions::COMPATIBILITY_EXCEL 'Excel'
  53. * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
  54. * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
  55. *
  56. * @return bool (Success or Failure)
  57. */
  58. public static function setCompatibilityMode($compatibilityMode)
  59. {
  60. if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
  61. ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
  62. ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
  63. ) {
  64. self::$compatibilityMode = $compatibilityMode;
  65. return true;
  66. }
  67. return false;
  68. }
  69. /**
  70. * Return the current Compatibility Mode.
  71. *
  72. * @category Function Configuration
  73. *
  74. * @return string Compatibility Mode
  75. * Possible Return values are:
  76. * Functions::COMPATIBILITY_EXCEL 'Excel'
  77. * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
  78. * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
  79. */
  80. public static function getCompatibilityMode()
  81. {
  82. return self::$compatibilityMode;
  83. }
  84. /**
  85. * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
  86. *
  87. * @category Function Configuration
  88. *
  89. * @param string $returnDateType Return Date Format
  90. * Permitted values are:
  91. * Functions::RETURNDATE_PHP_NUMERIC 'P'
  92. * Functions::RETURNDATE_PHP_OBJECT 'O'
  93. * Functions::RETURNDATE_EXCEL 'E'
  94. *
  95. * @return bool Success or failure
  96. */
  97. public static function setReturnDateType($returnDateType)
  98. {
  99. if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
  100. ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
  101. ($returnDateType == self::RETURNDATE_EXCEL)
  102. ) {
  103. self::$returnDateType = $returnDateType;
  104. return true;
  105. }
  106. return false;
  107. }
  108. /**
  109. * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
  110. *
  111. * @category Function Configuration
  112. *
  113. * @return string Return Date Format
  114. * Possible Return values are:
  115. * Functions::RETURNDATE_PHP_NUMERIC 'P'
  116. * Functions::RETURNDATE_PHP_OBJECT 'O'
  117. * Functions::RETURNDATE_EXCEL 'E'
  118. */
  119. public static function getReturnDateType()
  120. {
  121. return self::$returnDateType;
  122. }
  123. /**
  124. * DUMMY.
  125. *
  126. * @category Error Returns
  127. *
  128. * @return string #Not Yet Implemented
  129. */
  130. public static function DUMMY()
  131. {
  132. return '#Not Yet Implemented';
  133. }
  134. /**
  135. * DIV0.
  136. *
  137. * @category Error Returns
  138. *
  139. * @return string #Not Yet Implemented
  140. */
  141. public static function DIV0()
  142. {
  143. return self::$errorCodes['divisionbyzero'];
  144. }
  145. /**
  146. * NA.
  147. *
  148. * Excel Function:
  149. * =NA()
  150. *
  151. * Returns the error value #N/A
  152. * #N/A is the error value that means "no value is available."
  153. *
  154. * @category Logical Functions
  155. *
  156. * @return string #N/A!
  157. */
  158. public static function NA()
  159. {
  160. return self::$errorCodes['na'];
  161. }
  162. /**
  163. * NaN.
  164. *
  165. * Returns the error value #NUM!
  166. *
  167. * @category Error Returns
  168. *
  169. * @return string #NUM!
  170. */
  171. public static function NAN()
  172. {
  173. return self::$errorCodes['num'];
  174. }
  175. /**
  176. * NAME.
  177. *
  178. * Returns the error value #NAME?
  179. *
  180. * @category Error Returns
  181. *
  182. * @return string #NAME?
  183. */
  184. public static function NAME()
  185. {
  186. return self::$errorCodes['name'];
  187. }
  188. /**
  189. * REF.
  190. *
  191. * Returns the error value #REF!
  192. *
  193. * @category Error Returns
  194. *
  195. * @return string #REF!
  196. */
  197. public static function REF()
  198. {
  199. return self::$errorCodes['reference'];
  200. }
  201. /**
  202. * NULL.
  203. *
  204. * Returns the error value #NULL!
  205. *
  206. * @category Error Returns
  207. *
  208. * @return string #NULL!
  209. */
  210. public static function null()
  211. {
  212. return self::$errorCodes['null'];
  213. }
  214. /**
  215. * VALUE.
  216. *
  217. * Returns the error value #VALUE!
  218. *
  219. * @category Error Returns
  220. *
  221. * @return string #VALUE!
  222. */
  223. public static function VALUE()
  224. {
  225. return self::$errorCodes['value'];
  226. }
  227. public static function isMatrixValue($idx)
  228. {
  229. return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
  230. }
  231. public static function isValue($idx)
  232. {
  233. return substr_count($idx, '.') == 0;
  234. }
  235. public static function isCellValue($idx)
  236. {
  237. return substr_count($idx, '.') > 1;
  238. }
  239. public static function ifCondition($condition)
  240. {
  241. $condition = self::flattenSingleValue($condition);
  242. if (!isset($condition[0]) && !is_numeric($condition)) {
  243. $condition = '=""';
  244. }
  245. if (!in_array($condition[0], ['>', '<', '='])) {
  246. if (!is_numeric($condition)) {
  247. $condition = Calculation::wrapResult(strtoupper($condition));
  248. }
  249. return '=' . $condition;
  250. }
  251. preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
  252. list(, $operator, $operand) = $matches;
  253. if (!is_numeric($operand)) {
  254. $operand = str_replace('"', '""', $operand);
  255. $operand = Calculation::wrapResult(strtoupper($operand));
  256. }
  257. return $operator . $operand;
  258. }
  259. /**
  260. * ERROR_TYPE.
  261. *
  262. * @param mixed $value Value to check
  263. *
  264. * @return bool
  265. */
  266. public static function errorType($value = '')
  267. {
  268. $value = self::flattenSingleValue($value);
  269. $i = 1;
  270. foreach (self::$errorCodes as $errorCode) {
  271. if ($value === $errorCode) {
  272. return $i;
  273. }
  274. ++$i;
  275. }
  276. return self::NA();
  277. }
  278. /**
  279. * IS_BLANK.
  280. *
  281. * @param mixed $value Value to check
  282. *
  283. * @return bool
  284. */
  285. public static function isBlank($value = null)
  286. {
  287. if ($value !== null) {
  288. $value = self::flattenSingleValue($value);
  289. }
  290. return $value === null;
  291. }
  292. /**
  293. * IS_ERR.
  294. *
  295. * @param mixed $value Value to check
  296. *
  297. * @return bool
  298. */
  299. public static function isErr($value = '')
  300. {
  301. $value = self::flattenSingleValue($value);
  302. return self::isError($value) && (!self::isNa(($value)));
  303. }
  304. /**
  305. * IS_ERROR.
  306. *
  307. * @param mixed $value Value to check
  308. *
  309. * @return bool
  310. */
  311. public static function isError($value = '')
  312. {
  313. $value = self::flattenSingleValue($value);
  314. if (!is_string($value)) {
  315. return false;
  316. }
  317. return in_array($value, self::$errorCodes);
  318. }
  319. /**
  320. * IS_NA.
  321. *
  322. * @param mixed $value Value to check
  323. *
  324. * @return bool
  325. */
  326. public static function isNa($value = '')
  327. {
  328. $value = self::flattenSingleValue($value);
  329. return $value === self::NA();
  330. }
  331. /**
  332. * IS_EVEN.
  333. *
  334. * @param mixed $value Value to check
  335. *
  336. * @return bool|string
  337. */
  338. public static function isEven($value = null)
  339. {
  340. $value = self::flattenSingleValue($value);
  341. if ($value === null) {
  342. return self::NAME();
  343. } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
  344. return self::VALUE();
  345. }
  346. return $value % 2 == 0;
  347. }
  348. /**
  349. * IS_ODD.
  350. *
  351. * @param mixed $value Value to check
  352. *
  353. * @return bool|string
  354. */
  355. public static function isOdd($value = null)
  356. {
  357. $value = self::flattenSingleValue($value);
  358. if ($value === null) {
  359. return self::NAME();
  360. } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
  361. return self::VALUE();
  362. }
  363. return abs($value) % 2 == 1;
  364. }
  365. /**
  366. * IS_NUMBER.
  367. *
  368. * @param mixed $value Value to check
  369. *
  370. * @return bool
  371. */
  372. public static function isNumber($value = null)
  373. {
  374. $value = self::flattenSingleValue($value);
  375. if (is_string($value)) {
  376. return false;
  377. }
  378. return is_numeric($value);
  379. }
  380. /**
  381. * IS_LOGICAL.
  382. *
  383. * @param mixed $value Value to check
  384. *
  385. * @return bool
  386. */
  387. public static function isLogical($value = null)
  388. {
  389. $value = self::flattenSingleValue($value);
  390. return is_bool($value);
  391. }
  392. /**
  393. * IS_TEXT.
  394. *
  395. * @param mixed $value Value to check
  396. *
  397. * @return bool
  398. */
  399. public static function isText($value = null)
  400. {
  401. $value = self::flattenSingleValue($value);
  402. return is_string($value) && !self::isError($value);
  403. }
  404. /**
  405. * IS_NONTEXT.
  406. *
  407. * @param mixed $value Value to check
  408. *
  409. * @return bool
  410. */
  411. public static function isNonText($value = null)
  412. {
  413. return !self::isText($value);
  414. }
  415. /**
  416. * N.
  417. *
  418. * Returns a value converted to a number
  419. *
  420. * @param null|mixed $value The value you want converted
  421. *
  422. * @return number N converts values listed in the following table
  423. * If value is or refers to N returns
  424. * A number That number
  425. * A date The serial number of that date
  426. * TRUE 1
  427. * FALSE 0
  428. * An error value The error value
  429. * Anything else 0
  430. */
  431. public static function n($value = null)
  432. {
  433. while (is_array($value)) {
  434. $value = array_shift($value);
  435. }
  436. switch (gettype($value)) {
  437. case 'double':
  438. case 'float':
  439. case 'integer':
  440. return $value;
  441. case 'boolean':
  442. return (int) $value;
  443. case 'string':
  444. // Errors
  445. if ((strlen($value) > 0) && ($value[0] == '#')) {
  446. return $value;
  447. }
  448. break;
  449. }
  450. return 0;
  451. }
  452. /**
  453. * TYPE.
  454. *
  455. * Returns a number that identifies the type of a value
  456. *
  457. * @param null|mixed $value The value you want tested
  458. *
  459. * @return number N converts values listed in the following table
  460. * If value is or refers to N returns
  461. * A number 1
  462. * Text 2
  463. * Logical Value 4
  464. * An error value 16
  465. * Array or Matrix 64
  466. */
  467. public static function TYPE($value = null)
  468. {
  469. $value = self::flattenArrayIndexed($value);
  470. if (is_array($value) && (count($value) > 1)) {
  471. end($value);
  472. $a = key($value);
  473. // Range of cells is an error
  474. if (self::isCellValue($a)) {
  475. return 16;
  476. // Test for Matrix
  477. } elseif (self::isMatrixValue($a)) {
  478. return 64;
  479. }
  480. } elseif (empty($value)) {
  481. // Empty Cell
  482. return 1;
  483. }
  484. $value = self::flattenSingleValue($value);
  485. if (($value === null) || (is_float($value)) || (is_int($value))) {
  486. return 1;
  487. } elseif (is_bool($value)) {
  488. return 4;
  489. } elseif (is_array($value)) {
  490. return 64;
  491. } elseif (is_string($value)) {
  492. // Errors
  493. if ((strlen($value) > 0) && ($value[0] == '#')) {
  494. return 16;
  495. }
  496. return 2;
  497. }
  498. return 0;
  499. }
  500. /**
  501. * Convert a multi-dimensional array to a simple 1-dimensional array.
  502. *
  503. * @param array $array Array to be flattened
  504. *
  505. * @return array Flattened array
  506. */
  507. public static function flattenArray($array)
  508. {
  509. if (!is_array($array)) {
  510. return (array) $array;
  511. }
  512. $arrayValues = [];
  513. foreach ($array as $value) {
  514. if (is_array($value)) {
  515. foreach ($value as $val) {
  516. if (is_array($val)) {
  517. foreach ($val as $v) {
  518. $arrayValues[] = $v;
  519. }
  520. } else {
  521. $arrayValues[] = $val;
  522. }
  523. }
  524. } else {
  525. $arrayValues[] = $value;
  526. }
  527. }
  528. return $arrayValues;
  529. }
  530. /**
  531. * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
  532. *
  533. * @param array $array Array to be flattened
  534. *
  535. * @return array Flattened array
  536. */
  537. public static function flattenArrayIndexed($array)
  538. {
  539. if (!is_array($array)) {
  540. return (array) $array;
  541. }
  542. $arrayValues = [];
  543. foreach ($array as $k1 => $value) {
  544. if (is_array($value)) {
  545. foreach ($value as $k2 => $val) {
  546. if (is_array($val)) {
  547. foreach ($val as $k3 => $v) {
  548. $arrayValues[$k1 . '.' . $k2 . '.' . $k3] = $v;
  549. }
  550. } else {
  551. $arrayValues[$k1 . '.' . $k2] = $val;
  552. }
  553. }
  554. } else {
  555. $arrayValues[$k1] = $value;
  556. }
  557. }
  558. return $arrayValues;
  559. }
  560. /**
  561. * Convert an array to a single scalar value by extracting the first element.
  562. *
  563. * @param mixed $value Array or scalar value
  564. *
  565. * @return mixed
  566. */
  567. public static function flattenSingleValue($value = '')
  568. {
  569. while (is_array($value)) {
  570. $value = array_pop($value);
  571. }
  572. return $value;
  573. }
  574. /**
  575. * ISFORMULA.
  576. *
  577. * @param mixed $cellReference The cell to check
  578. * @param Cell $pCell The current cell (containing this formula)
  579. *
  580. * @return bool|string
  581. */
  582. public static function isFormula($cellReference = '', Cell $pCell = null)
  583. {
  584. if ($pCell === null) {
  585. return self::REF();
  586. }
  587. preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
  588. $cellReference = $matches[6] . $matches[7];
  589. $worksheetName = trim($matches[3], "'");
  590. $worksheet = (!empty($worksheetName))
  591. ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
  592. : $pCell->getWorksheet();
  593. return $worksheet->getCell($cellReference)->isFormula();
  594. }
  595. }