NumberFormatProvider::formatCurrency()   A
last analyzed

Complexity

Conditions 4
Paths 2

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 18
ccs 8
cts 8
cp 1
crap 4
rs 10
c 0
b 0
f 0
eloc 8
nc 2
nop 4
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);
0 ignored issues
show
Bug introduced by
It seems like $numberArr can also be of type true; however, parameter $array of array_reverse() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

136
        $numberArr = array_reverse(/** @scrutinizer ignore-type */ $numberArr);
Loading history...
137 216
        $formatted = '';
138
139 216
        for ($i = 0;$i < count($numberArr);$i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
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
}