CurrencyFormatter   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
eloc 59
c 2
b 2
f 0
dl 0
loc 194
rs 10
wmc 22

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getFormatter() 0 8 2
A setCurrency() 0 5 1
A getDefaultCurrencyPosition() 0 3 1
A getDefaultCurrencySymbol() 0 3 1
A getCurrencyGroupingSeparator() 0 3 1
A unformatCurrency() 0 37 6
A formattedNumber() 0 7 1
A getCurrencyDecimalSeparator() 0 3 1
A getCurrencySymbol() 0 6 2
A getCurrency() 0 3 1
A formattedCurrency() 0 31 5
1
<?php
2
3
namespace LeKoala\FormElements;
4
5
use NumberFormatter;
6
use SilverStripe\ORM\FieldType\DBCurrency;
7
8
trait CurrencyFormatter
9
{
10
    use Localize;
11
12
    /**
13
     * a char(3) currency code
14
     *
15
     * @var string
16
     */
17
    protected $currency;
18
19
    /**
20
     * Get currency formatter
21
     *
22
     * @return NumberFormatter
23
     */
24
    public function getFormatter()
25
    {
26
        $locale = $this->getLocale();
27
        $currency = $this->getCurrency();
28
        if ($currency) {
29
            $locale .= '@currency=' . $currency;
30
        }
31
        return NumberFormatter::create($locale, NumberFormatter::CURRENCY);
32
    }
33
34
    /**
35
     * Get the currency code
36
     *
37
     */
38
    public function getCurrency()
39
    {
40
        return $this->currency;
41
    }
42
43
    /**
44
     * The currency code (eg: USD, EUR...)
45
     *
46
     * @return $this
47
     */
48
    public function setCurrency($currency)
49
    {
50
        $this->currency = $currency;
51
52
        return $this;
53
    }
54
55
    /**
56
     * Get currency symbol
57
     *
58
     * This is only returned if currency is explicitely set to avoid returning the wrong currency
59
     * just because of user's locale
60
     *
61
     * @return string
62
     */
63
    public function getCurrencySymbol()
64
    {
65
        if ($this->currency) {
66
            return $this->getFormatter()->getSymbol(NumberFormatter::CURRENCY_SYMBOL);
67
        }
68
        return $this->getDefaultCurrencySymbol();
69
    }
70
71
    /**
72
     * Get default symbol
73
     *
74
     * @return string
75
     */
76
    public function getDefaultCurrencySymbol()
77
    {
78
        return DBCurrency::config()->uninherited('currency_symbol');
79
    }
80
81
    /**
82
     * Get default symbol
83
     *
84
     * @return string
85
     */
86
    public function getDefaultCurrencyPosition()
87
    {
88
        return DBCurrency::config()->uninherited('currency_position');
89
    }
90
91
    /**
92
     * Get grouping separator
93
     *
94
     * @return string
95
     */
96
    public function getCurrencyGroupingSeparator()
97
    {
98
        return $this->getFormatter()->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
99
    }
100
101
    /**
102
     * Get decimal separator
103
     *
104
     * @return string
105
     */
106
    public function getCurrencyDecimalSeparator()
107
    {
108
        return $this->getFormatter()->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
109
    }
110
111
    /**
112
     * Get nicely formatted currency (based on current locale)
113
     *
114
     * @param float $amount
115
     * @param int $decimals
116
     * @return string
117
     */
118
    public function formattedCurrency($amount, $decimals = 2)
119
    {
120
        $negative = false;
121
        if ($amount < 0) {
122
            $negative = true;
123
        }
124
125
        $currency = $this->getCurrency();
126
127
        // Without currency, format as basic localised number
128
        // Otherwise we could end up displaying dollars in euros due to current locale
129
        // We only format according to the locale if the currency is set
130
        if (!$currency) {
131
            $symbol = $this->getCurrencySymbol();
132
            $pos = $this->getDefaultCurrencyPosition();
133
            $fmt = $this->formattedNumber($amount, $decimals);
134
            if ($pos == 'after') {
135
                $ret = "$fmt $symbol";
136
            } else {
137
                $ret = "$symbol $fmt";
138
            }
139
        } else {
140
            $formatter = $this->getFormatter();
141
            $ret = $formatter->formatCurrency($amount, $currency);
142
        }
143
144
        if ($negative) {
145
            $ret = "-$ret";
146
        }
147
148
        return $ret;
149
    }
150
151
    public function formattedNumber($amount, $decimals = 2)
152
    {
153
        return number_format(
154
            abs($amount ?? 0),
155
            $decimals,
156
            $this->getCurrencyDecimalSeparator(),
157
            $this->getCurrencyGroupingSeparator()
158
        );
159
    }
160
161
    /**
162
     * @link https://github.com/mcuadros/currency-detector/blob/master/src/CurrencyDetector/Detector.php
163
     * @param string|array $value
164
     */
165
    public static function unformatCurrency($value)
166
    {
167
        // If we pass an array of values, apply to the whole array
168
        if (is_array($value)) {
169
            foreach ($value as &$val) {
170
                $val = self::unformatCurrency($val);
171
            }
172
            return $value;
173
        }
174
175
        // Return early
176
        if (!$value) {
177
            return 0;
178
        }
179
180
        // If it contains -, it's a negative number
181
        $neg = false;
182
        if (strpos($value, '-') !== false) {
183
            $neg = true;
184
        }
185
186
        // Remove all separators except the last one
187
        $cleanString = preg_replace('/([^0-9\.,])/i', '', $value);
188
        $onlyNumbersString = preg_replace('/([^0-9])/i', '', $value);
189
        $separatorsCountToBeErased = strlen($cleanString) - strlen($onlyNumbersString) - 1;
190
        $stringWithCommaOrDot = preg_replace('/([,\.])/', '', $cleanString, $separatorsCountToBeErased);
191
192
        // Remove any thousand separator followed by 3 digits before the end of the string
193
        $removeThousandsSeparator = preg_replace('/(\.|,)(?=[0-9]{3,}$)/', '', $stringWithCommaOrDot);
194
195
        $value = str_replace(',', '.', $removeThousandsSeparator);
196
197
        // For a negative value, return it as such
198
        if ($neg) {
199
            $value = -1 * abs($value);
0 ignored issues
show
Bug introduced by
$value of type string is incompatible with the type double|integer expected by parameter $num of abs(). ( Ignorable by Annotation )

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

199
            $value = -1 * abs(/** @scrutinizer ignore-type */ $value);
Loading history...
200
        }
201
        return $value;
202
    }
203
}
204