Passed
Pull Request — master (#137)
by Jordan
06:16
created

NumberFormatProvider::formatNumber()   B

Complexity

Conditions 7
Paths 34

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7.0422

Importance

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