/wp-content/plugins/quiz-maker/includes/PHPExcel/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php

https://github.com/livinglab/openlab · PHP · 672 lines · 367 code · 98 blank · 207 comment · 82 complexity · db351416ff32ea9eee7b40c1073f3a24 MD5 · raw file

  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Calculation;
  3. use PhpOffice\PhpSpreadsheet\Shared\Date;
  4. use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
  5. use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  6. class TextData
  7. {
  8. private static $invalidChars;
  9. private static function unicodeToOrd($character)
  10. {
  11. return unpack('V', iconv('UTF-8', 'UCS-4LE', $character))[1];
  12. }
  13. /**
  14. * CHARACTER.
  15. *
  16. * @param string $character Value
  17. *
  18. * @return string
  19. */
  20. public static function CHARACTER($character)
  21. {
  22. $character = Functions::flattenSingleValue($character);
  23. if ((!is_numeric($character)) || ($character < 0)) {
  24. return Functions::VALUE();
  25. }
  26. if (function_exists('iconv')) {
  27. return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
  28. }
  29. return mb_convert_encoding('&#' . (int) $character . ';', 'UTF-8', 'HTML-ENTITIES');
  30. }
  31. /**
  32. * TRIMNONPRINTABLE.
  33. *
  34. * @param mixed $stringValue Value to check
  35. *
  36. * @return string
  37. */
  38. public static function TRIMNONPRINTABLE($stringValue = '')
  39. {
  40. $stringValue = Functions::flattenSingleValue($stringValue);
  41. if (is_bool($stringValue)) {
  42. return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
  43. }
  44. if (self::$invalidChars == null) {
  45. self::$invalidChars = range(chr(0), chr(31));
  46. }
  47. if (is_string($stringValue) || is_numeric($stringValue)) {
  48. return str_replace(self::$invalidChars, '', trim($stringValue, "\x00..\x1F"));
  49. }
  50. return null;
  51. }
  52. /**
  53. * TRIMSPACES.
  54. *
  55. * @param mixed $stringValue Value to check
  56. *
  57. * @return string
  58. */
  59. public static function TRIMSPACES($stringValue = '')
  60. {
  61. $stringValue = Functions::flattenSingleValue($stringValue);
  62. if (is_bool($stringValue)) {
  63. return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
  64. }
  65. if (is_string($stringValue) || is_numeric($stringValue)) {
  66. return trim(preg_replace('/ +/', ' ', trim($stringValue, ' ')), ' ');
  67. }
  68. return null;
  69. }
  70. /**
  71. * ASCIICODE.
  72. *
  73. * @param string $characters Value
  74. *
  75. * @return int
  76. */
  77. public static function ASCIICODE($characters)
  78. {
  79. if (($characters === null) || ($characters === '')) {
  80. return Functions::VALUE();
  81. }
  82. $characters = Functions::flattenSingleValue($characters);
  83. if (is_bool($characters)) {
  84. if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
  85. $characters = (int) $characters;
  86. } else {
  87. $characters = ($characters) ? Calculation::getTRUE() : Calculation::getFALSE();
  88. }
  89. }
  90. $character = $characters;
  91. if (mb_strlen($characters, 'UTF-8') > 1) {
  92. $character = mb_substr($characters, 0, 1, 'UTF-8');
  93. }
  94. return self::unicodeToOrd($character);
  95. }
  96. /**
  97. * CONCATENATE.
  98. *
  99. * @return string
  100. */
  101. public static function CONCATENATE(...$args)
  102. {
  103. $returnValue = '';
  104. // Loop through arguments
  105. $aArgs = Functions::flattenArray($args);
  106. foreach ($aArgs as $arg) {
  107. if (is_bool($arg)) {
  108. if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
  109. $arg = (int) $arg;
  110. } else {
  111. $arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
  112. }
  113. }
  114. $returnValue .= $arg;
  115. }
  116. return $returnValue;
  117. }
  118. /**
  119. * DOLLAR.
  120. *
  121. * This function converts a number to text using currency format, with the decimals rounded to the specified place.
  122. * The format used is $#,##0.00_);($#,##0.00)..
  123. *
  124. * @param float $value The value to format
  125. * @param int $decimals The number of digits to display to the right of the decimal point.
  126. * If decimals is negative, number is rounded to the left of the decimal point.
  127. * If you omit decimals, it is assumed to be 2
  128. *
  129. * @return string
  130. */
  131. public static function DOLLAR($value = 0, $decimals = 2)
  132. {
  133. $value = Functions::flattenSingleValue($value);
  134. $decimals = $decimals === null ? 0 : Functions::flattenSingleValue($decimals);
  135. // Validate parameters
  136. if (!is_numeric($value) || !is_numeric($decimals)) {
  137. return Functions::NAN();
  138. }
  139. $decimals = floor($decimals);
  140. $mask = '$#,##0';
  141. if ($decimals > 0) {
  142. $mask .= '.' . str_repeat('0', $decimals);
  143. } else {
  144. $round = pow(10, abs($decimals));
  145. if ($value < 0) {
  146. $round = 0 - $round;
  147. }
  148. $value = MathTrig::MROUND($value, $round);
  149. }
  150. return NumberFormat::toFormattedString($value, $mask);
  151. }
  152. /**
  153. * SEARCHSENSITIVE.
  154. *
  155. * @param string $needle The string to look for
  156. * @param string $haystack The string in which to look
  157. * @param int $offset Offset within $haystack
  158. *
  159. * @return string
  160. */
  161. public static function SEARCHSENSITIVE($needle, $haystack, $offset = 1)
  162. {
  163. $needle = Functions::flattenSingleValue($needle);
  164. $haystack = Functions::flattenSingleValue($haystack);
  165. $offset = Functions::flattenSingleValue($offset);
  166. if (!is_bool($needle)) {
  167. if (is_bool($haystack)) {
  168. $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE();
  169. }
  170. if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
  171. if (StringHelper::countCharacters($needle) == 0) {
  172. return $offset;
  173. }
  174. $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
  175. if ($pos !== false) {
  176. return ++$pos;
  177. }
  178. }
  179. }
  180. return Functions::VALUE();
  181. }
  182. /**
  183. * SEARCHINSENSITIVE.
  184. *
  185. * @param string $needle The string to look for
  186. * @param string $haystack The string in which to look
  187. * @param int $offset Offset within $haystack
  188. *
  189. * @return string
  190. */
  191. public static function SEARCHINSENSITIVE($needle, $haystack, $offset = 1)
  192. {
  193. $needle = Functions::flattenSingleValue($needle);
  194. $haystack = Functions::flattenSingleValue($haystack);
  195. $offset = Functions::flattenSingleValue($offset);
  196. if (!is_bool($needle)) {
  197. if (is_bool($haystack)) {
  198. $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE();
  199. }
  200. if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
  201. if (StringHelper::countCharacters($needle) == 0) {
  202. return $offset;
  203. }
  204. $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
  205. if ($pos !== false) {
  206. return ++$pos;
  207. }
  208. }
  209. }
  210. return Functions::VALUE();
  211. }
  212. /**
  213. * FIXEDFORMAT.
  214. *
  215. * @param mixed $value Value to check
  216. * @param int $decimals
  217. * @param bool $no_commas
  218. *
  219. * @return string
  220. */
  221. public static function FIXEDFORMAT($value, $decimals = 2, $no_commas = false)
  222. {
  223. $value = Functions::flattenSingleValue($value);
  224. $decimals = Functions::flattenSingleValue($decimals);
  225. $no_commas = Functions::flattenSingleValue($no_commas);
  226. // Validate parameters
  227. if (!is_numeric($value) || !is_numeric($decimals)) {
  228. return Functions::NAN();
  229. }
  230. $decimals = floor($decimals);
  231. $valueResult = round($value, $decimals);
  232. if ($decimals < 0) {
  233. $decimals = 0;
  234. }
  235. if (!$no_commas) {
  236. $valueResult = number_format($valueResult, $decimals);
  237. }
  238. return (string) $valueResult;
  239. }
  240. /**
  241. * LEFT.
  242. *
  243. * @param string $value Value
  244. * @param int $chars Number of characters
  245. *
  246. * @return string
  247. */
  248. public static function LEFT($value = '', $chars = 1)
  249. {
  250. $value = Functions::flattenSingleValue($value);
  251. $chars = Functions::flattenSingleValue($chars);
  252. if ($chars < 0) {
  253. return Functions::VALUE();
  254. }
  255. if (is_bool($value)) {
  256. $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
  257. }
  258. return mb_substr($value, 0, $chars, 'UTF-8');
  259. }
  260. /**
  261. * MID.
  262. *
  263. * @param string $value Value
  264. * @param int $start Start character
  265. * @param int $chars Number of characters
  266. *
  267. * @return string
  268. */
  269. public static function MID($value = '', $start = 1, $chars = null)
  270. {
  271. $value = Functions::flattenSingleValue($value);
  272. $start = Functions::flattenSingleValue($start);
  273. $chars = Functions::flattenSingleValue($chars);
  274. if (($start < 1) || ($chars < 0)) {
  275. return Functions::VALUE();
  276. }
  277. if (is_bool($value)) {
  278. $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
  279. }
  280. if (empty($chars)) {
  281. return '';
  282. }
  283. return mb_substr($value, --$start, $chars, 'UTF-8');
  284. }
  285. /**
  286. * RIGHT.
  287. *
  288. * @param string $value Value
  289. * @param int $chars Number of characters
  290. *
  291. * @return string
  292. */
  293. public static function RIGHT($value = '', $chars = 1)
  294. {
  295. $value = Functions::flattenSingleValue($value);
  296. $chars = Functions::flattenSingleValue($chars);
  297. if ($chars < 0) {
  298. return Functions::VALUE();
  299. }
  300. if (is_bool($value)) {
  301. $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
  302. }
  303. return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
  304. }
  305. /**
  306. * STRINGLENGTH.
  307. *
  308. * @param string $value Value
  309. *
  310. * @return int
  311. */
  312. public static function STRINGLENGTH($value = '')
  313. {
  314. $value = Functions::flattenSingleValue($value);
  315. if (is_bool($value)) {
  316. $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
  317. }
  318. return mb_strlen($value, 'UTF-8');
  319. }
  320. /**
  321. * LOWERCASE.
  322. *
  323. * Converts a string value to upper case.
  324. *
  325. * @param string $mixedCaseString
  326. *
  327. * @return string
  328. */
  329. public static function LOWERCASE($mixedCaseString)
  330. {
  331. $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
  332. if (is_bool($mixedCaseString)) {
  333. $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
  334. }
  335. return StringHelper::strToLower($mixedCaseString);
  336. }
  337. /**
  338. * UPPERCASE.
  339. *
  340. * Converts a string value to upper case.
  341. *
  342. * @param string $mixedCaseString
  343. *
  344. * @return string
  345. */
  346. public static function UPPERCASE($mixedCaseString)
  347. {
  348. $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
  349. if (is_bool($mixedCaseString)) {
  350. $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
  351. }
  352. return StringHelper::strToUpper($mixedCaseString);
  353. }
  354. /**
  355. * PROPERCASE.
  356. *
  357. * Converts a string value to upper case.
  358. *
  359. * @param string $mixedCaseString
  360. *
  361. * @return string
  362. */
  363. public static function PROPERCASE($mixedCaseString)
  364. {
  365. $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
  366. if (is_bool($mixedCaseString)) {
  367. $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
  368. }
  369. return StringHelper::strToTitle($mixedCaseString);
  370. }
  371. /**
  372. * REPLACE.
  373. *
  374. * @param string $oldText String to modify
  375. * @param int $start Start character
  376. * @param int $chars Number of characters
  377. * @param string $newText String to replace in defined position
  378. *
  379. * @return string
  380. */
  381. public static function REPLACE($oldText, $start, $chars, $newText)
  382. {
  383. $oldText = Functions::flattenSingleValue($oldText);
  384. $start = Functions::flattenSingleValue($start);
  385. $chars = Functions::flattenSingleValue($chars);
  386. $newText = Functions::flattenSingleValue($newText);
  387. $left = self::LEFT($oldText, $start - 1);
  388. $right = self::RIGHT($oldText, self::STRINGLENGTH($oldText) - ($start + $chars) + 1);
  389. return $left . $newText . $right;
  390. }
  391. /**
  392. * SUBSTITUTE.
  393. *
  394. * @param string $text Value
  395. * @param string $fromText From Value
  396. * @param string $toText To Value
  397. * @param int $instance Instance Number
  398. *
  399. * @return string
  400. */
  401. public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0)
  402. {
  403. $text = Functions::flattenSingleValue($text);
  404. $fromText = Functions::flattenSingleValue($fromText);
  405. $toText = Functions::flattenSingleValue($toText);
  406. $instance = floor(Functions::flattenSingleValue($instance));
  407. if ($instance == 0) {
  408. return str_replace($fromText, $toText, $text);
  409. }
  410. $pos = -1;
  411. while ($instance > 0) {
  412. $pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
  413. if ($pos === false) {
  414. break;
  415. }
  416. --$instance;
  417. }
  418. if ($pos !== false) {
  419. return self::REPLACE($text, ++$pos, mb_strlen($fromText, 'UTF-8'), $toText);
  420. }
  421. return $text;
  422. }
  423. /**
  424. * RETURNSTRING.
  425. *
  426. * @param mixed $testValue Value to check
  427. *
  428. * @return null|string
  429. */
  430. public static function RETURNSTRING($testValue = '')
  431. {
  432. $testValue = Functions::flattenSingleValue($testValue);
  433. if (is_string($testValue)) {
  434. return $testValue;
  435. }
  436. return null;
  437. }
  438. /**
  439. * TEXTFORMAT.
  440. *
  441. * @param mixed $value Value to check
  442. * @param string $format Format mask to use
  443. *
  444. * @return string
  445. */
  446. public static function TEXTFORMAT($value, $format)
  447. {
  448. $value = Functions::flattenSingleValue($value);
  449. $format = Functions::flattenSingleValue($format);
  450. if ((is_string($value)) && (!is_numeric($value)) && Date::isDateTimeFormatCode($format)) {
  451. $value = DateTime::DATEVALUE($value);
  452. }
  453. return (string) NumberFormat::toFormattedString($value, $format);
  454. }
  455. /**
  456. * VALUE.
  457. *
  458. * @param mixed $value Value to check
  459. *
  460. * @return bool
  461. */
  462. public static function VALUE($value = '')
  463. {
  464. $value = Functions::flattenSingleValue($value);
  465. if (!is_numeric($value)) {
  466. $numberValue = str_replace(
  467. StringHelper::getThousandsSeparator(),
  468. '',
  469. trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
  470. );
  471. if (is_numeric($numberValue)) {
  472. return (float) $numberValue;
  473. }
  474. $dateSetting = Functions::getReturnDateType();
  475. Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
  476. if (strpos($value, ':') !== false) {
  477. $timeValue = DateTime::TIMEVALUE($value);
  478. if ($timeValue !== Functions::VALUE()) {
  479. Functions::setReturnDateType($dateSetting);
  480. return $timeValue;
  481. }
  482. }
  483. $dateValue = DateTime::DATEVALUE($value);
  484. if ($dateValue !== Functions::VALUE()) {
  485. Functions::setReturnDateType($dateSetting);
  486. return $dateValue;
  487. }
  488. Functions::setReturnDateType($dateSetting);
  489. return Functions::VALUE();
  490. }
  491. return (float) $value;
  492. }
  493. /**
  494. * NUMBERVALUE.
  495. *
  496. * @param mixed $value Value to check
  497. * @param string $decimalSeparator decimal separator, defaults to locale defined value
  498. * @param string $groupSeparator group/thosands separator, defaults to locale defined value
  499. *
  500. * @return float|string
  501. */
  502. public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
  503. {
  504. $value = Functions::flattenSingleValue($value);
  505. $decimalSeparator = Functions::flattenSingleValue($decimalSeparator);
  506. $groupSeparator = Functions::flattenSingleValue($groupSeparator);
  507. if (!is_numeric($value)) {
  508. $decimalSeparator = empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : $decimalSeparator;
  509. $groupSeparator = empty($groupSeparator) ? StringHelper::getThousandsSeparator() : $groupSeparator;
  510. $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
  511. if ($decimalPositions > 1) {
  512. return Functions::VALUE();
  513. }
  514. $decimalOffset = array_pop($matches[0])[1];
  515. if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
  516. return Functions::VALUE();
  517. }
  518. $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
  519. // Handle the special case of trailing % signs
  520. $percentageString = rtrim($value, '%');
  521. if (!is_numeric($percentageString)) {
  522. return Functions::VALUE();
  523. }
  524. $percentageAdjustment = strlen($value) - strlen($percentageString);
  525. if ($percentageAdjustment) {
  526. $value = (float) $percentageString;
  527. $value /= pow(10, $percentageAdjustment * 2);
  528. }
  529. }
  530. return (float) $value;
  531. }
  532. /**
  533. * Compares two text strings and returns TRUE if they are exactly the same, FALSE otherwise.
  534. * EXACT is case-sensitive but ignores formatting differences.
  535. * Use EXACT to test text being entered into a document.
  536. *
  537. * @param $value1
  538. * @param $value2
  539. *
  540. * @return bool
  541. */
  542. public static function EXACT($value1, $value2)
  543. {
  544. $value1 = Functions::flattenSingleValue($value1);
  545. $value2 = Functions::flattenSingleValue($value2);
  546. return (string) $value2 === (string) $value1;
  547. }
  548. /**
  549. * TEXTJOIN.
  550. *
  551. * @param mixed $delimiter
  552. * @param mixed $ignoreEmpty
  553. * @param mixed $args
  554. *
  555. * @return string
  556. */
  557. public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args)
  558. {
  559. // Loop through arguments
  560. $aArgs = Functions::flattenArray($args);
  561. foreach ($aArgs as $key => &$arg) {
  562. if ($ignoreEmpty && trim($arg) == '') {
  563. unset($aArgs[$key]);
  564. } elseif (is_bool($arg)) {
  565. if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
  566. $arg = (int) $arg;
  567. } else {
  568. $arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
  569. }
  570. }
  571. }
  572. return implode($delimiter, $aArgs);
  573. }
  574. }