/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php

https://gitlab.com/jjpa2018/dashboard · PHP · 279 lines · 168 code · 34 blank · 77 comment · 31 complexity · b635bf8339d56992451afd3e76796de9 MD5 · raw file

  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
  3. use DateTimeInterface;
  4. use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
  5. use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
  6. use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
  7. use PhpOffice\PhpSpreadsheet\Calculation\Functions;
  8. use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
  9. use PhpOffice\PhpSpreadsheet\Shared\Date;
  10. use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
  11. use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  12. class Format
  13. {
  14. use ArrayEnabled;
  15. /**
  16. * DOLLAR.
  17. *
  18. * This function converts a number to text using currency format, with the decimals rounded to the specified place.
  19. * The format used is $#,##0.00_);($#,##0.00)..
  20. *
  21. * @param mixed $value The value to format
  22. * Or can be an array of values
  23. * @param mixed $decimals The number of digits to display to the right of the decimal point (as an integer).
  24. * If decimals is negative, number is rounded to the left of the decimal point.
  25. * If you omit decimals, it is assumed to be 2
  26. * Or can be an array of values
  27. *
  28. * @return array|string
  29. * If an array of values is passed for either of the arguments, then the returned result
  30. * will also be an array with matching dimensions
  31. */
  32. public static function DOLLAR($value = 0, $decimals = 2)
  33. {
  34. if (is_array($value) || is_array($decimals)) {
  35. return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals);
  36. }
  37. try {
  38. $value = Helpers::extractFloat($value);
  39. $decimals = Helpers::extractInt($decimals, -100, 0, true);
  40. } catch (CalcExp $e) {
  41. return $e->getMessage();
  42. }
  43. $mask = '$#,##0';
  44. if ($decimals > 0) {
  45. $mask .= '.' . str_repeat('0', $decimals);
  46. } else {
  47. $round = 10 ** abs($decimals);
  48. if ($value < 0) {
  49. $round = 0 - $round;
  50. }
  51. $value = MathTrig\Round::multiple($value, $round);
  52. }
  53. $mask = "{$mask};-{$mask}";
  54. return NumberFormat::toFormattedString($value, $mask);
  55. }
  56. /**
  57. * FIXED.
  58. *
  59. * @param mixed $value The value to format
  60. * Or can be an array of values
  61. * @param mixed $decimals Integer value for the number of decimal places that should be formatted
  62. * Or can be an array of values
  63. * @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
  64. * Or can be an array of values
  65. *
  66. * @return array|string
  67. * If an array of values is passed for either of the arguments, then the returned result
  68. * will also be an array with matching dimensions
  69. */
  70. public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false)
  71. {
  72. if (is_array($value) || is_array($decimals) || is_array($noCommas)) {
  73. return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals, $noCommas);
  74. }
  75. try {
  76. $value = Helpers::extractFloat($value);
  77. $decimals = Helpers::extractInt($decimals, -100, 0, true);
  78. } catch (CalcExp $e) {
  79. return $e->getMessage();
  80. }
  81. $valueResult = round($value, $decimals);
  82. if ($decimals < 0) {
  83. $decimals = 0;
  84. }
  85. if ($noCommas === false) {
  86. $valueResult = number_format(
  87. $valueResult,
  88. $decimals,
  89. StringHelper::getDecimalSeparator(),
  90. StringHelper::getThousandsSeparator()
  91. );
  92. }
  93. return (string) $valueResult;
  94. }
  95. /**
  96. * TEXT.
  97. *
  98. * @param mixed $value The value to format
  99. * Or can be an array of values
  100. * @param mixed $format A string with the Format mask that should be used
  101. * Or can be an array of values
  102. *
  103. * @return array|string
  104. * If an array of values is passed for either of the arguments, then the returned result
  105. * will also be an array with matching dimensions
  106. */
  107. public static function TEXTFORMAT($value, $format)
  108. {
  109. if (is_array($value) || is_array($format)) {
  110. return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
  111. }
  112. $value = Helpers::extractString($value);
  113. $format = Helpers::extractString($format);
  114. if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) {
  115. $value = DateTimeExcel\DateValue::fromString($value);
  116. }
  117. return (string) NumberFormat::toFormattedString($value, $format);
  118. }
  119. /**
  120. * @param mixed $value Value to check
  121. *
  122. * @return mixed
  123. */
  124. private static function convertValue($value)
  125. {
  126. $value = $value ?? 0;
  127. if (is_bool($value)) {
  128. if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
  129. $value = (int) $value;
  130. } else {
  131. throw new CalcExp(Functions::VALUE());
  132. }
  133. }
  134. return $value;
  135. }
  136. /**
  137. * VALUE.
  138. *
  139. * @param mixed $value Value to check
  140. * Or can be an array of values
  141. *
  142. * @return array|DateTimeInterface|float|int|string A string if arguments are invalid
  143. * If an array of values is passed for the argument, then the returned result
  144. * will also be an array with matching dimensions
  145. */
  146. public static function VALUE($value = '')
  147. {
  148. if (is_array($value)) {
  149. return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
  150. }
  151. try {
  152. $value = self::convertValue($value);
  153. } catch (CalcExp $e) {
  154. return $e->getMessage();
  155. }
  156. if (!is_numeric($value)) {
  157. $numberValue = str_replace(
  158. StringHelper::getThousandsSeparator(),
  159. '',
  160. trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
  161. );
  162. if (is_numeric($numberValue)) {
  163. return (float) $numberValue;
  164. }
  165. $dateSetting = Functions::getReturnDateType();
  166. Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
  167. if (strpos($value, ':') !== false) {
  168. $timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
  169. if ($timeValue !== Functions::VALUE()) {
  170. Functions::setReturnDateType($dateSetting);
  171. return $timeValue;
  172. }
  173. }
  174. $dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value));
  175. if ($dateValue !== Functions::VALUE()) {
  176. Functions::setReturnDateType($dateSetting);
  177. return $dateValue;
  178. }
  179. Functions::setReturnDateType($dateSetting);
  180. return Functions::VALUE();
  181. }
  182. return (float) $value;
  183. }
  184. /**
  185. * @param mixed $decimalSeparator
  186. */
  187. private static function getDecimalSeparator($decimalSeparator): string
  188. {
  189. return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
  190. }
  191. /**
  192. * @param mixed $groupSeparator
  193. */
  194. private static function getGroupSeparator($groupSeparator): string
  195. {
  196. return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
  197. }
  198. /**
  199. * NUMBERVALUE.
  200. *
  201. * @param mixed $value The value to format
  202. * Or can be an array of values
  203. * @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
  204. * Or can be an array of values
  205. * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
  206. * Or can be an array of values
  207. *
  208. * @return array|float|string
  209. */
  210. public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
  211. {
  212. if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
  213. return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
  214. }
  215. try {
  216. $value = self::convertValue($value);
  217. $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
  218. $groupSeparator = self::getGroupSeparator($groupSeparator);
  219. } catch (CalcExp $e) {
  220. return $e->getMessage();
  221. }
  222. if (!is_numeric($value)) {
  223. $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
  224. if ($decimalPositions > 1) {
  225. return Functions::VALUE();
  226. }
  227. $decimalOffset = array_pop($matches[0])[1];
  228. if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
  229. return Functions::VALUE();
  230. }
  231. $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
  232. // Handle the special case of trailing % signs
  233. $percentageString = rtrim($value, '%');
  234. if (!is_numeric($percentageString)) {
  235. return Functions::VALUE();
  236. }
  237. $percentageAdjustment = strlen($value) - strlen($percentageString);
  238. if ($percentageAdjustment) {
  239. $value = (float) $percentageString;
  240. $value /= 10 ** ($percentageAdjustment * 2);
  241. }
  242. }
  243. return is_array($value) ? Functions::VALUE() : (float) $value;
  244. }
  245. }