Passed
Push — master ( c5f6b1...caf8c4 )
by herry
02:52
created

Convert::calculate()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
c 0
b 0
f 0
nc 3
nop 3
dl 0
loc 11
rs 10
1
<?php
2
/**
3
 * This file is part of the mucts.com.
4
 *
5
 * This source file is subject to the MIT license that is bundled
6
 * with this source code in the file LICENSE.
7
 *
8
 * @version 1.0
9
 * @author herry<[email protected]>
10
 * @copyright © 2020 MuCTS.com All Rights Reserved.
11
 */
12
13
namespace MuCTS\Money\Chinese;
14
15
use Illuminate\Support\Arr;
16
use MuCTS\Money\Exceptions\InvalidArgumentException;
17
18
final class Convert
19
{
20
    private const DIGITAL = [
21
        0 => '零',
22
        1 => '壹',
23
        2 => '贰',
24
        3 => '叁',
25
        4 => '肆',
26
        5 => '伍',
27
        6 => '陆',
28
        7 => '柒',
29
        8 => '捌',
30
        9 => '玖',
31
    ];
32
33
    private const UNIT = [
34
        -4 => '毫',
35
        -3 => '厘',
36
        -2 => '分',
37
        -1 => '角',
38
        0 => '元',
39
        1 => '拾',
40
        2 => '佰',
41
        3 => '仟',
42
        4 => '万',
43
        8 => '亿',
44
        12 => '兆',
45
        16 => '京',
46
        20 => '垓',
47
        24 => '杼',
48
        28 => '穰',
49
        32 => '沟',
50
        36 => '涧',
51
        40 => '正',
52
        44 => '载',
53
        48 => '极'
54
    ];
55
56
    private const SYMBOL = [
57
        '-' => '负',
58
        '+' => '',
59
        '' => '整'
60
    ];
61
62
    /**
63
     * 整数部分转换
64
     *
65
     * @param string $integer
66
     * @return string
67
     */
68
    private static function integerToCn(string $integer): string
69
    {
70
        if (($i = $len = strlen($integer)) > 48) {
71
            throw new InvalidArgumentException(sprintf('%s is not a valid chinese number text', $integer));
72
        }
73
        $integerStr = '';
74
        $unit = 0;
75
        while ($i) {
76
            $num = $integer[$len - $i--];
77
            if (in_array($num, array_keys(self::SYMBOL), true)) {
78
                $integerStr .= self::SYMBOL[$num];
79
                continue;
80
            }
81
            if ($num > 0 || Arr::exists(self::UNIT, $i) && $unit <= $i) {
82
                $integerStr .= $num > 0 || $i == 0 ? self::DIGITAL[$num] : '';
83
                $unit = Arr::exists(self::UNIT, $i) ? $i : $i % 4;
84
                $integerStr .= self::UNIT[$unit];
85
            }
86
        }
87
        return $integerStr;
88
    }
89
90
    /**
91
     * 小数部分转换
92
     *
93
     * @param string $decimals
94
     * @param string|null $default
95
     * @return string|null
96
     */
97
    private static function decimalToCn(string $decimals, ?string $default = null): ?string
98
    {
99
        $decimalStr = '';
100
        $len = strlen($decimals);
101
        for ($i = 0; $i < $len; $i++) {
102
            $num = $decimals[$i];
103
            if ($num > 0) {
104
                $decimalStr .= self::DIGITAL[$num] . self::UNIT[-1 - $i];
105
            }
106
        }
107
        return $decimalStr != '' ? $decimalStr : $default;
0 ignored issues
show
introduced by
The condition $decimalStr != '' is always false.
Loading history...
108
    }
109
110
111
    /**
112
     * 金额转换成中文
113
     *
114
     * @param string|int|float $amount
115
     * @param string $prefix
116
     * @param string $cnPrefix
117
     * @return string
118
     */
119
    public static function toCn($amount, $prefix = '¥', string $cnPrefix = '人民币'): string
120
    {
121
        if (!preg_match(sprintf('/^(%s)?[+\-]?([1-9]\d{0,2}([,]?\d{3})*|0)(\.\d{0,4})?$/', $prefix), $amount)) {
122
            throw new InvalidArgumentException(sprintf('%s is not a valid chinese number text', $amount));
123
        }
124
        $amount = strtr($amount, [',' => '', $prefix => '']);
125
        list($integer, $decimals) = explode('.', strval($amount) . '.', 2);
126
        return $cnPrefix . self::integerToCn($integer) . strval(self::decimalToCn($decimals, self::SYMBOL['']));
127
    }
128
129
    /**
130
     * 中文金额转阿拉伯数字金额
131
     *
132
     * @param string $cnAmount
133
     * @param string $prefix
134
     * @param string $cnPrefix
135
     * @return string
136
     */
137
    public static function toDigit(string $cnAmount, string $prefix = '¥', string $cnPrefix = '人民币'): string
138
    {
139
        $amount = preg_replace(sprintf("/^%s/", $cnPrefix), '', $cnAmount);
140
        $amounts = mb_str_split($amount);
141
        $maxUnit = 0;
142
        $isPlus = 1;
143
        $digits = $units = [];
144
        $isDigit = false;
145
        $amounts[0] == self::SYMBOL['-'] && array_unshift($amounts) && $isPlus = -1;
0 ignored issues
show
Bug introduced by
The call to array_unshift() has too few arguments starting with var. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

145
        $amounts[0] == self::SYMBOL['-'] && /** @scrutinizer ignore-call */ array_unshift($amounts) && $isPlus = -1;

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
146
        Arr::last($amounts) == self::SYMBOL[''] && array_pop($amounts);
147
148
        while ($chr = array_unshift($amounts)) {
149
            if (($key = array_search($chr, self::DIGITAL)) !== false) {
150
                array_push($digits, $key);
151
                $isDigit = true;
152
            } elseif (($un = array_search($chr, self::UNIT)) !== false) {
153
                $maxUnit = max($maxUnit, $un);
154
                $isDigit && array_push($units, $maxUnit > $un ? $maxUnit + $un : $un);
155
                $isDigit = false;
156
            } else {
157
                throw new InvalidArgumentException(sprintf('%s is not a valid chinese number text', $cnAmount));
158
            }
159
        }
160
        if (!empty($amounts) || count($digits) != count($units)) {
161
            throw new InvalidArgumentException(sprintf('%s is not a valid chinese number text', $cnAmount));
162
        }
163
        return $prefix . self::calculate($digits, $units, $isPlus);
164
    }
165
166
    /**
167
     * 转换成数字计算
168
     *
169
     * @param $digits
170
     * @param $units
171
     * @param int $isPlus
172
     * @return string
173
     */
174
    private static function calculate($digits, $units, $isPlus = 1): string
175
    {
176
        $integer = $decimal = 0;
177
        while (($digit = array_pop($digits)) && ($unit = array_pop($units))) {
178
            if ($units >= 0) {
179
                $integer = gmp_add($integer, gmp_mul($digit, gmp_pow(10, $unit)));
180
            } else {
181
                $decimal += $digit * (10 ** $unit);
182
            }
183
        }
184
        return gmp_strval(gmp_mul($integer, $isPlus)) . ltrim(strval($decimal), '0');
185
    }
186
}