1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Samsara\Fermat\Core\Provider; |
4
|
|
|
|
5
|
|
|
use Samsara\Fermat\Core\Enums\Currency; |
6
|
|
|
use Samsara\Fermat\Core\Enums\NumberFormat; |
7
|
|
|
use Samsara\Fermat\Core\Enums\NumberGrouping; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* |
11
|
|
|
*/ |
12
|
|
|
class NumberFormatProvider |
13
|
|
|
{ |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @param string $number |
17
|
|
|
* @param Currency $currency |
18
|
|
|
* @param NumberFormat|null $format |
19
|
|
|
* @param NumberGrouping|null $grouping |
20
|
|
|
* @return string |
21
|
|
|
*/ |
22
|
96 |
|
public static function formatCurrency( |
23
|
|
|
string $number, |
24
|
|
|
Currency $currency, |
25
|
|
|
?NumberFormat $format = null, |
26
|
|
|
?NumberGrouping $grouping = null |
27
|
|
|
): string |
28
|
|
|
{ |
29
|
96 |
|
$number = self::formatNumber($number, $format, $grouping); |
30
|
|
|
|
31
|
96 |
|
if (str_starts_with($number, '(') || str_starts_with($number, '-') || str_starts_with($number, '+')) { |
32
|
72 |
|
$number = str_replace('(', '('.$currency->value, $number); |
33
|
72 |
|
$number = str_replace('-', '-'.$currency->value, $number); |
34
|
72 |
|
$number = str_replace('+', '+'.$currency->value, $number); |
35
|
|
|
} else { |
36
|
24 |
|
$number = $currency->value . $number; |
37
|
|
|
} |
38
|
|
|
|
39
|
96 |
|
return $number; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @param string $number |
44
|
|
|
* @param NumberFormat|null $format |
45
|
|
|
* @param NumberGrouping|null $grouping |
46
|
|
|
* @return string |
47
|
|
|
*/ |
48
|
216 |
|
public static function formatNumber( |
49
|
|
|
string $number, |
50
|
|
|
?NumberFormat $format = null, |
51
|
|
|
?NumberGrouping $grouping = null |
52
|
|
|
): string |
53
|
|
|
{ |
54
|
216 |
|
$posNegChar = ''; |
55
|
|
|
|
56
|
216 |
|
if (!is_null($format)) { |
57
|
216 |
|
$negative = str_starts_with($number, '-'); |
58
|
216 |
|
$number = str_replace('-', '', $number); |
59
|
216 |
|
if (str_contains($number, '.')) { |
60
|
162 |
|
[$wholePart, $decimalPart] = explode('.', $number); |
61
|
|
|
} else { |
62
|
54 |
|
$wholePart = $number; |
63
|
54 |
|
$decimalPart = ''; |
64
|
|
|
} |
65
|
216 |
|
if (!is_null($grouping)) { |
66
|
216 |
|
$wholePart = self::addDelimiter($wholePart, $format, $grouping); |
67
|
|
|
} |
68
|
|
|
|
69
|
216 |
|
$posNegChar = $negative ? self::getNegativeCharacter($format) : self::getPositiveCharacter($format); |
70
|
|
|
|
71
|
216 |
|
$number = $wholePart; |
72
|
|
|
|
73
|
216 |
|
if ($decimalPart) { |
74
|
162 |
|
$number .= self::getRadixCharacter($format) . $decimalPart; |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
|
78
|
216 |
|
if (str_contains($posNegChar, '\N')) { |
79
|
48 |
|
$number = str_replace('\N', $number, $posNegChar); |
80
|
|
|
} else { |
81
|
168 |
|
$number = $posNegChar . $number; |
82
|
|
|
} |
83
|
|
|
|
84
|
216 |
|
return $number; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @param string $number |
89
|
|
|
* @return string |
90
|
|
|
*/ |
91
|
32 |
|
public static function formatScientific(string $number): string |
92
|
|
|
{ |
93
|
32 |
|
$negative = str_starts_with($number, '-') ? '-' : ''; |
94
|
32 |
|
$imaginary = str_ends_with($number, 'i') ? 'i' : ''; |
95
|
32 |
|
$number = str_replace('-', '', $number); |
96
|
32 |
|
$number = str_replace('i', '', $number); |
97
|
32 |
|
if (str_contains($number, '.')) { |
98
|
24 |
|
[$wholePart, $decimalPart] = explode('.', $number); |
99
|
|
|
} else { |
100
|
8 |
|
$wholePart = $number; |
101
|
8 |
|
$decimalPart = ''; |
102
|
|
|
} |
103
|
|
|
|
104
|
32 |
|
$wholeSizeNonZero = strlen(ltrim($wholePart, '0')); |
105
|
32 |
|
$decimalSizeNonZero = strlen(ltrim($decimalPart, '0')); |
106
|
|
|
|
107
|
32 |
|
if ($wholeSizeNonZero) { |
108
|
16 |
|
$exponent = $wholeSizeNonZero; |
109
|
16 |
|
$exponent -= 1; |
110
|
16 |
|
$mantissa = substr($wholePart, 0, 1) . '.' . substr($wholePart, 1) . $decimalPart; |
111
|
|
|
} else { |
112
|
16 |
|
$exponent = strlen($decimalPart) - $decimalSizeNonZero; |
113
|
16 |
|
$exponent += 1; |
114
|
16 |
|
$mantissa = substr($decimalPart, $exponent-1, 1) . '.' . substr($decimalPart, $exponent); |
115
|
16 |
|
$exponent *= -1; |
116
|
|
|
} |
117
|
|
|
|
118
|
32 |
|
$mantissa = rtrim($mantissa, '0'); |
119
|
|
|
|
120
|
32 |
|
return $negative . $mantissa . 'E' . $exponent . $imaginary; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @param string $number |
125
|
|
|
* @param NumberFormat $format |
126
|
|
|
* @param NumberGrouping $grouping |
127
|
|
|
* @return string |
128
|
|
|
*/ |
129
|
216 |
|
public static function addDelimiter( |
130
|
|
|
string $number, |
131
|
|
|
NumberFormat $format = NumberFormat::English, |
132
|
|
|
NumberGrouping $grouping = NumberGrouping::Standard |
133
|
|
|
): string |
134
|
|
|
{ |
135
|
216 |
|
$numberArr = str_split($number); |
136
|
216 |
|
$numberArr = array_reverse($numberArr); |
|
|
|
|
137
|
216 |
|
$formatted = ''; |
138
|
|
|
|
139
|
216 |
|
for ($i = 0;$i < count($numberArr);$i++) { |
|
|
|
|
140
|
216 |
|
$j = $i + 1; |
141
|
|
|
|
142
|
216 |
|
$formatted = $numberArr[$i].$formatted; |
143
|
|
|
|
144
|
216 |
|
if ($grouping == NumberGrouping::Standard) { |
145
|
200 |
|
if ($j % 3 == 0 && array_key_exists($i+1, $numberArr)) { |
146
|
200 |
|
$formatted = self::getDelimiterCharacter($format).$formatted; |
147
|
|
|
} |
148
|
24 |
|
} elseif ($grouping == NumberGrouping::Indian) { |
149
|
24 |
|
if ($j == 3 && array_key_exists($i+1, $numberArr)) { |
150
|
12 |
|
$formatted = self::getDelimiterCharacter($format).$formatted; |
151
|
24 |
|
} elseif (($j - 3) % 2 == 0 && ($j - 3) > 0 && array_key_exists($i+1, $numberArr)) { |
152
|
12 |
|
$formatted = self::getDelimiterCharacter($format).$formatted; |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
216 |
|
return $formatted; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @param NumberFormat $format |
162
|
|
|
* @return string |
163
|
|
|
*/ |
164
|
108 |
|
public static function getPositiveCharacter(NumberFormat $format): string |
165
|
|
|
{ |
166
|
|
|
return match ($format) { |
167
|
|
|
NumberFormat::EnglishFinance, |
168
|
|
|
NumberFormat::EuropeanFinance, |
169
|
108 |
|
NumberFormat::TechnicalFinance => '+', |
170
|
108 |
|
default => '' |
171
|
|
|
}; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* @param NumberFormat $format |
176
|
|
|
* @return string |
177
|
|
|
*/ |
178
|
108 |
|
public static function getNegativeCharacter(NumberFormat $format): string |
179
|
|
|
{ |
180
|
|
|
return match ($format) { |
181
|
|
|
NumberFormat::EnglishFinance, |
182
|
|
|
NumberFormat::EuropeanFinance, |
183
|
108 |
|
NumberFormat::TechnicalFinance => '(\N)', |
184
|
108 |
|
default => '-', |
185
|
|
|
}; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @param NumberFormat $format |
190
|
|
|
* @return string |
191
|
|
|
*/ |
192
|
164 |
|
public static function getRadixCharacter(NumberFormat $format): string |
193
|
|
|
{ |
194
|
|
|
return match ($format) { |
195
|
|
|
NumberFormat::EuropeanFinance, |
196
|
164 |
|
NumberFormat::European => ',', |
197
|
164 |
|
default => '.' |
198
|
|
|
}; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* @param NumberFormat $format |
203
|
|
|
* @return string |
204
|
|
|
*/ |
205
|
112 |
|
public static function getDelimiterCharacter(NumberFormat $format): string |
206
|
|
|
{ |
207
|
|
|
return match ($format) { |
208
|
|
|
NumberFormat::EnglishFinance, |
209
|
112 |
|
NumberFormat::English => ',', |
210
|
|
|
NumberFormat::EuropeanFinance, |
211
|
36 |
|
NumberFormat::European => '.', |
212
|
|
|
NumberFormat::TechnicalFinance, |
213
|
112 |
|
NumberFormat::Technical => ' ', |
214
|
|
|
}; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
} |