Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
4
 
5
use Composer\Pcre\Preg;
6
use DateTimeInterface;
7
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
8
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
9
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
10
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
11
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
12
use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
13
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
14
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
15
use PhpOffice\PhpSpreadsheet\RichText\RichText;
16
use PhpOffice\PhpSpreadsheet\Shared\Date;
17
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
18
use 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|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(mixed $value = 0, mixed $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|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(mixed $value, mixed $decimals = 2, mixed $noCommas = false): array|string
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
        if ($noCommas === false) {
100
            $valueResult = number_format(
101
                $valueResult,
102
                $decimals,
103
                StringHelper::getDecimalSeparator(),
104
                StringHelper::getThousandsSeparator()
105
            );
106
        }
107
 
108
        return (string) $valueResult;
109
    }
110
 
111
    /**
112
     * TEXT.
113
     *
114
     * @param mixed $value The value to format
115
     *                         Or can be an array of values
116
     * @param mixed $format A string with the Format mask that should be used
117
     *                         Or can be an array of values
118
     *
119
     * @return array|string If an array of values is passed for either of the arguments, then the returned result
120
     *            will also be an array with matching dimensions
121
     */
122
    public static function TEXTFORMAT(mixed $value, mixed $format): array|string
123
    {
124
        if (is_array($value) || is_array($format)) {
125
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
126
        }
127
 
128
        try {
129
            $value = Helpers::extractString($value, true);
130
            $format = Helpers::extractString($format, true);
131
        } catch (CalcExp $e) {
132
            return $e->getMessage();
133
        }
134
 
135
        $format = (string) NumberFormat::convertSystemFormats($format);
136
 
137
        if (!is_numeric($value) && Date::isDateTimeFormatCode($format) && !Preg::isMatch('/^\s*\d+(\s+\d+)+\s*$/', $value)) {
138
            $value1 = DateTimeExcel\DateValue::fromString($value);
139
            $value2 = DateTimeExcel\TimeValue::fromString($value);
140
            /** @var float|int|string */
141
            $value = (is_numeric($value1) && is_numeric($value2)) ? ($value1 + $value2) : (is_numeric($value1) ? $value1 : (is_numeric($value2) ? $value2 : $value));
142
        }
143
 
144
        return (string) NumberFormat::toFormattedString($value, $format);
145
    }
146
 
147
    /**
148
     * @param mixed $value Value to check
149
     */
150
    private static function convertValue(mixed $value, bool $spacesMeanZero = false): mixed
151
    {
152
        $value = $value ?? 0;
153
        if (is_bool($value)) {
154
            if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
155
                $value = (int) $value;
156
            } else {
157
                throw new CalcExp(ExcelError::VALUE());
158
            }
159
        }
160
        if (is_string($value)) {
161
            $value = trim($value);
162
            if (ErrorValue::isError($value, true)) {
163
                throw new CalcExp($value);
164
            }
165
            if ($spacesMeanZero && $value === '') {
166
                $value = 0;
167
            }
168
        }
169
 
170
        return $value;
171
    }
172
 
173
    /**
174
     * VALUE.
175
     *
176
     * @param mixed $value Value to check
177
     *                         Or can be an array of values
178
     *
179
     * @return array|DateTimeInterface|float|int|string A string if arguments are invalid
180
     *         If an array of values is passed for the argument, then the returned result
181
     *            will also be an array with matching dimensions
182
     */
183
    public static function VALUE(mixed $value = '')
184
    {
185
        if (is_array($value)) {
186
            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
187
        }
188
 
189
        try {
190
            $value = self::convertValue($value);
191
        } catch (CalcExp $e) {
192
            return $e->getMessage();
193
        }
194
        if (!is_numeric($value)) {
195
            $numberValue = str_replace(
196
                StringHelper::getThousandsSeparator(),
197
                '',
198
                trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
199
            );
200
            if ($numberValue === '') {
201
                return ExcelError::VALUE();
202
            }
203
            if (is_numeric($numberValue)) {
204
                return (float) $numberValue;
205
            }
206
 
207
            $dateSetting = Functions::getReturnDateType();
208
            Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
209
 
210
            if (str_contains($value, ':')) {
211
                $timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
212
                if ($timeValue !== ExcelError::VALUE()) {
213
                    Functions::setReturnDateType($dateSetting);
214
 
215
                    return $timeValue;
216
                }
217
            }
218
            $dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value));
219
            if ($dateValue !== ExcelError::VALUE()) {
220
                Functions::setReturnDateType($dateSetting);
221
 
222
                return $dateValue;
223
            }
224
            Functions::setReturnDateType($dateSetting);
225
 
226
            return ExcelError::VALUE();
227
        }
228
 
229
        return (float) $value;
230
    }
231
 
232
    /**
233
     * VALUETOTEXT.
234
     *
235
     * @param mixed $value The value to format
236
     *                         Or can be an array of values
237
     *
238
     * @return array|string If an array of values is passed for either of the arguments, then the returned result
239
     *            will also be an array with matching dimensions
240
     */
241
    public static function valueToText(mixed $value, mixed $format = false): array|string
242
    {
243
        if (is_array($value) || is_array($format)) {
244
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
245
        }
246
 
247
        $format = (bool) $format;
248
 
249
        if (is_object($value) && $value instanceof RichText) {
250
            $value = $value->getPlainText();
251
        }
252
        if (is_string($value)) {
253
            $value = ($format === true) ? Calculation::wrapResult($value) : $value;
254
            $value = str_replace("\n", '', $value);
255
        } elseif (is_bool($value)) {
256
            $value = Calculation::getLocaleBoolean($value ? 'TRUE' : 'FALSE');
257
        }
258
 
259
        return (string) $value;
260
    }
261
 
262
    private static function getDecimalSeparator(mixed $decimalSeparator): string
263
    {
264
        return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
265
    }
266
 
267
    private static function getGroupSeparator(mixed $groupSeparator): string
268
    {
269
        return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
270
    }
271
 
272
    /**
273
     * NUMBERVALUE.
274
     *
275
     * @param mixed $value The value to format
276
     *                         Or can be an array of values
277
     * @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
278
     *                         Or can be an array of values
279
     * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
280
     *                         Or can be an array of values
281
     */
282
    public static function NUMBERVALUE(mixed $value = '', mixed $decimalSeparator = null, mixed $groupSeparator = null): array|string|float
283
    {
284
        if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
285
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
286
        }
287
 
288
        try {
289
            $value = self::convertValue($value, true);
290
            $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
291
            $groupSeparator = self::getGroupSeparator($groupSeparator);
292
        } catch (CalcExp $e) {
293
            return $e->getMessage();
294
        }
295
 
296
        if (!is_numeric($value)) {
297
            $decimalPositions = Preg::matchAllWithOffsets('/' . preg_quote($decimalSeparator, '/') . '/', $value, $matches);
298
            if ($decimalPositions > 1) {
299
                return ExcelError::VALUE();
300
            }
301
            $decimalOffset = array_pop($matches[0])[1] ?? null;
302
            if ($decimalOffset === null || strpos($value, $groupSeparator, $decimalOffset) !== false) {
303
                return ExcelError::VALUE();
304
            }
305
 
306
            $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
307
 
308
            // Handle the special case of trailing % signs
309
            $percentageString = rtrim($value, '%');
310
            if (!is_numeric($percentageString)) {
311
                return ExcelError::VALUE();
312
            }
313
 
314
            $percentageAdjustment = strlen($value) - strlen($percentageString);
315
            if ($percentageAdjustment) {
316
                $value = (float) $percentageString;
317
                $value /= 10 ** ($percentageAdjustment * 2);
318
            }
319
        }
320
 
321
        return is_array($value) ? ExcelError::VALUE() : (float) $value;
322
    }
323
}