chengkun
2025-09-15 3c9050e82e582414dc7b208c8283fe47be37eeba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?php
 
namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
 
class FractionFormatter extends BaseFormatter
{
    /** @param null|bool|float|int|string $value  value to be formatted */
    public static function format(mixed $value, string $format): string
    {
        $format = self::stripQuotes($format);
        $value = (float) $value;
        $absValue = abs($value);
 
        $sign = ($value < 0.0) ? '-' : '';
 
        $integerPart = floor($absValue);
 
        $decimalPart = self::getDecimal((string) $absValue);
        if ($decimalPart === '0') {
            return "{$sign}{$integerPart}";
        }
        $decimalLength = strlen($decimalPart);
        $decimalDivisor = 10 ** $decimalLength;
 
        preg_match('/(#?.*\?)\/(\?+|\d+)/', $format, $matches);
        $formatIntegerPart = $matches[1];
 
        if (is_numeric($matches[2])) {
            $fractionDivisor = 100 / (int) $matches[2];
        } else {
            /** @var float $fractionDivisor */
            $fractionDivisor = MathTrig\Gcd::evaluate((int) $decimalPart, $decimalDivisor);
        }
 
        $adjustedDecimalPart = (int) round((int) $decimalPart / $fractionDivisor, 0);
        $adjustedDecimalDivisor = $decimalDivisor / $fractionDivisor;
 
        if ((str_contains($formatIntegerPart, '0'))) {
            return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
        } elseif ((str_contains($formatIntegerPart, '#'))) {
            if ($integerPart == 0) {
                return "{$sign}{$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
            }
 
            return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
        } elseif ((str_starts_with($formatIntegerPart, '? ?'))) {
            if ($integerPart == 0) {
                $integerPart = '';
            }
 
            return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
        }
 
        $adjustedDecimalPart += $integerPart * $adjustedDecimalDivisor;
 
        return "{$sign}{$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
    }
 
    private static function getDecimal(string $value): string
    {
        $decimalPart = '0';
        if (preg_match('/^\\d*[.](\\d*[1-9])0*$/', $value, $matches) === 1) {
            $decimalPart = $matches[1];
        }
 
        return $decimalPart;
    }
}