Functions.php 16 KB

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