PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Calculation / TextData / Format.php

Format.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Calculation/TextData/Format.php

380 lines 12.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation\TextData;
4
5 use TablePress\Composer\Pcre\Preg;
6 use DateTimeInterface;
7 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
8 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation;
9 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
10 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
11 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Functions;
12 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
13 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
14 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
15 use TablePress\PhpOffice\PhpSpreadsheet\RichText\RichText;
16 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Date;
17 use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
18 use TablePress\PhpOffice\PhpSpreadsheet\Style\NumberFormat;
19
20 class Format
21 {
22 use ArrayEnabled;
23
24 /**
25 * DOLLAR.
26 *
27 * This function converts a number to text using currency format, with the decimals rounded to the specified place.
28 * The format used is $#,##0.00_);($#,##0.00)..
29 *
30 * @param mixed $value The value to format
31 * Or can be an array of values
32 * @param mixed $decimals The number of digits to display to the right of the decimal point (as an integer).
33 * If decimals is negative, number is rounded to the left of the decimal point.
34 * If you omit decimals, it is assumed to be 2
35 * Or can be an array of values
36 *
37 * @return array<mixed>|string If an array of values is passed for either of the arguments, then the returned result
38 * will also be an array with matching dimensions
39 */
40 public static function DOLLAR($value = 0, $decimals = 2)
41 {
42 if (is_array($value) || is_array($decimals)) {
43 return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals);
44 }
45
46 try {
47 $value = Helpers::extractFloat($value);
48 $decimals = Helpers::extractInt($decimals, -100, 0, true);
49 } catch (CalcExp $e) {
50 return $e->getMessage();
51 }
52
53 $mask = '$#,##0';
54 if ($decimals > 0) {
55 $mask .= '.' . str_repeat('0', $decimals);
56 } else {
57 $round = 10 ** abs($decimals);
58 if ($value < 0) {
59 $round = 0 - $round;
60 }
61 /** @var float|int|string */
62 $value = MathTrig\Round::multiple($value, $round);
63 }
64 $mask = "{$mask};-{$mask}";
65
66 return NumberFormat::toFormattedString($value, $mask);
67 }
68
69 /**
70 * FIXED.
71 *
72 * @param mixed $value The value to format
73 * Or can be an array of values
74 * @param mixed $decimals Integer value for the number of decimal places that should be formatted
75 * Or can be an array of values
76 * @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
77 * Or can be an array of values
78 *
79 * @return array<mixed>|string If an array of values is passed for either of the arguments, then the returned result
80 * will also be an array with matching dimensions
81 */
82 public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false)
83 {
84 if (is_array($value) || is_array($decimals) || is_array($noCommas)) {
85 return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals, $noCommas);
86 }
87
88 try {
89 $value = Helpers::extractFloat($value);
90 $decimals = Helpers::extractInt($decimals, -100, 0, true);
91 } catch (CalcExp $e) {
92 return $e->getMessage();
93 }
94
95 $valueResult = round($value, $decimals);
96 if ($decimals < 0) {
97 $decimals = 0;
98 }
99
100 return number_format(
101 $valueResult,
102 $decimals,
103 StringHelper::getDecimalSeparator(),
104 $noCommas ? '' : StringHelper::getThousandsSeparator()
105 );
106 }
107
108 /**
109 * TEXT.
110 *
111 * @param mixed $value The value to format
112 * Or can be an array of values
113 * @param mixed $format A string with the Format mask that should be used
114 * Or can be an array of values
115 *
116 * @return array<mixed>|string If an array of values is passed for either of the arguments, then the returned result
117 * will also be an array with matching dimensions
118 */
119 public static function TEXTFORMAT($value, $format)
120 {
121 if (is_array($value) || is_array($format)) {
122 return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
123 }
124
125 try {
126 $value = Helpers::extractString($value, true);
127 $format = Helpers::extractString($format, true);
128 } catch (CalcExp $e) {
129 return $e->getMessage();
130 }
131
132 $format = (string) NumberFormat::convertSystemFormats($format);
133
134 if (!is_numeric($value) && Date::isDateTimeFormatCode($format) && !Preg::isMatch('/^\s*\d+(\s+\d+)+\s*$/', $value)) {
135 $value1 = DateTimeExcel\DateValue::fromString($value);
136 $value2 = DateTimeExcel\TimeValue::fromString($value);
137 /** @var float|int|string */
138 $value = (is_numeric($value1) && is_numeric($value2)) ? ($value1 + $value2) : (is_numeric($value1) ? $value1 : (is_numeric($value2) ? $value2 : $value));
139 }
140
141 return (string) NumberFormat::toFormattedString($value, $format);
142 }
143
144 /**
145 * @param mixed $value Value to check
146 * @return mixed
147 */
148 private static function convertValue($value, bool $spacesMeanZero = false)
149 {
150 $value = $value ?? 0;
151 if (is_bool($value)) {
152 if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
153 $value = (int) $value;
154 } else {
155 throw new CalcExp(ExcelError::VALUE());
156 }
157 }
158 if (is_string($value)) {
159 $value = trim($value);
160 if (ErrorValue::isError($value, true)) {
161 throw new CalcExp($value);
162 }
163 if ($spacesMeanZero && $value === '') {
164 $value = 0;
165 }
166 }
167
168 return $value;
169 }
170
171 /**
172 * VALUE.
173 *
174 * @param mixed $value Value to check
175 * Or can be an array of values
176 *
177 * @return array<mixed>|DateTimeInterface|float|int|string A string if arguments are invalid
178 * If an array of values is passed for the argument, then the returned result
179 * will also be an array with matching dimensions
180 */
181 public static function VALUE($value = '')
182 {
183 if (is_array($value)) {
184 return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
185 }
186
187 try {
188 $value = self::convertValue($value);
189 } catch (CalcExp $e) {
190 return $e->getMessage();
191 }
192 if (!is_numeric($value)) {
193 $value = StringHelper::convertToString($value);
194 $numberValue = str_replace(
195 StringHelper::getThousandsSeparator(),
196 '',
197 trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
198 );
199 if ($numberValue === '') {
200 return ExcelError::VALUE();
201 }
202 if (is_numeric($numberValue)) {
203 return (float) $numberValue;
204 }
205
206 $dateSetting = Functions::getReturnDateType();
207 Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
208
209 if (str_contains($value, ':')) {
210 /** @var DateTimeInterface|float|int|string */
211 $timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
212 if ($timeValue !== ExcelError::VALUE()) {
213 Functions::setReturnDateType($dateSetting);
214
215 return $timeValue;
216 }
217 }
218 /** @var DateTimeInterface|float|int|string */
219 $dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value));
220 if ($dateValue !== ExcelError::VALUE()) {
221 Functions::setReturnDateType($dateSetting);
222
223 return $dateValue;
224 }
225 Functions::setReturnDateType($dateSetting);
226
227 return ExcelError::VALUE();
228 }
229
230 return (float) $value;
231 }
232
233 /**
234 * VALUETOTEXT.
235 *
236 * @param mixed $value The value to format
237 * Or can be an array of values
238 *
239 * @return array<mixed>|string If an array of values is passed for either of the arguments, then the returned result
240 * will also be an array with matching dimensions
241 * @param mixed $format
242 */
243 public static function valueToText($value, $format = false)
244 {
245 if (is_array($value) || is_array($format)) {
246 return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
247 }
248
249 $format = (bool) $format;
250
251 if (is_object($value) && $value instanceof RichText) {
252 $value = $value->getPlainText();
253 }
254 if (is_string($value)) {
255 $value = ($format === true) ? StringHelper::convertToString(Calculation::wrapResult($value)) : $value;
256 $value = str_replace("\n", '', $value);
257 } elseif (is_bool($value)) {
258 $value = Calculation::getLocaleBoolean($value ? 'TRUE' : 'FALSE');
259 }
260
261 return StringHelper::convertToString($value);
262 }
263
264 /**
265 * @param mixed $decimalSeparator
266 */
267 private static function getDecimalSeparator($decimalSeparator): string
268 {
269 return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : StringHelper::convertToString($decimalSeparator);
270 }
271
272 /**
273 * @param mixed $groupSeparator
274 */
275 private static function getGroupSeparator($groupSeparator): string
276 {
277 return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : StringHelper::convertToString($groupSeparator);
278 }
279
280 /**
281 * NUMBERVALUE.
282 *
283 * @param mixed $value The value to format
284 * Or can be an array of values
285 * @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
286 * Or can be an array of values
287 * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
288 * Or can be an array of values
289 *
290 * @return array<mixed>|float|string
291 */
292 public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
293 {
294 if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
295 return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
296 }
297
298 try {
299 $value = self::convertValue($value, true);
300 $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
301 $groupSeparator = self::getGroupSeparator($groupSeparator);
302 } catch (CalcExp $e) {
303 return $e->getMessage();
304 }
305
306 /** @var null|array<scalar>|scalar $value */
307 if (!is_array($value) && !is_numeric($value)) {
308 $value = StringHelper::convertToString($value);
309 $decimalPositions = Preg::matchAllWithOffsets('/' . preg_quote($decimalSeparator, '/') . '/', $value, $matches);
310 if ($decimalPositions > 1) {
311 return ExcelError::VALUE();
312 }
313 $decimalOffset = array_pop($matches[0])[1] ?? null;
314 if ($decimalOffset === null || strpos($value, $groupSeparator, $decimalOffset) !== false) {
315 return ExcelError::VALUE();
316 }
317
318 $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
319
320 // Handle the special case of trailing % signs
321 $percentageString = rtrim($value, '%');
322 if (!is_numeric($percentageString)) {
323 return ExcelError::VALUE();
324 }
325
326 $percentageAdjustment = strlen($value) - strlen($percentageString);
327 if ($percentageAdjustment) {
328 $value = (float) $percentageString;
329 $value /= 10 ** ($percentageAdjustment * 2);
330 }
331 }
332
333 return is_array($value) ? ExcelError::VALUE() : (float) $value;
334 }
335
336 /**
337 * NUMBER_FORMAT (Specific to TablePress).
338 *
339 * Formats a number with the . (period) as the decimal separator and the , (comma) as the thousands separator, rounded to a precision.
340 *
341 * The is a common number format in English-language regions.
342 *
343 * @param mixed $value The value to format.
344 * @param mixed $decimals Optional. Integer value for the number of decimal places that should be formatted. Default 0.
345 * @return string Formatted number.
346 */
347 public static function NUMBER_FORMAT( $value, $decimals = 0 ) {
348 $current_decimal_separator = StringHelper::getDecimalSeparator();
349 $current_thousands_separator = StringHelper::getThousandsSeparator();
350 StringHelper::setDecimalSeparator( '.' );
351 StringHelper::setThousandsSeparator( ',' );
352 $result = self::FIXEDFORMAT( $value, $decimals, false );
353 StringHelper::setDecimalSeparator( $current_decimal_separator );
354 StringHelper::setThousandsSeparator( $current_thousands_separator );
355 return $result;
356 }
357
358 /**
359 * NUMBER_FORMAT_EU (Specific to TablePress).
360 *
361 * Formats a number with the , (comma) as the decimal separator and the . (period) as the thousands separator, rounded to a precision.
362 *
363 * The is a common number format in non-English-language regions, mainly in Europe.
364 *
365 * @param mixed $value The value to format.
366 * @param mixed $decimals Optional. Integer value for the number of decimal places that should be formatted. Default 0.
367 * @return string Formatted number.
368 */
369 public static function NUMBER_FORMAT_EU( $value, $decimals = 0 ) {
370 $current_decimal_separator = StringHelper::getDecimalSeparator();
371 $current_thousands_separator = StringHelper::getThousandsSeparator();
372 StringHelper::setDecimalSeparator( ',' );
373 StringHelper::setThousandsSeparator( '.' );
374 $result = self::FIXEDFORMAT( $value, $decimals, false );
375 StringHelper::setDecimalSeparator( $current_decimal_separator );
376 StringHelper::setThousandsSeparator( $current_thousands_separator );
377 return $result;
378 }
379 }
380