Passed
Push — master ( 1c5b13...a1fa5d )
by Thomas
03:40 queued 01:18
created

CurrencyFormatter::formattedNumber()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 5
c 1
b 1
f 0
nc 1
nop 2
dl 0
loc 7
rs 10
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();
0 ignored issues
show
Bug introduced by
The method getCurrency() does not exist on LeKoala\FormElements\CurrencyFormatter. Did you maybe mean getCurrencySymbol()? ( Ignorable by Annotation )

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

27
        /** @scrutinizer ignore-call */ 
28
        $currency = $this->getCurrency();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

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

190
            $value = -1 * abs(/** @scrutinizer ignore-type */ $value);
Loading history...
191
        }
192
        return $value;
193
    }
194
}
195