Issues (3)

src/Money.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Whallysson\Money;
6
7
use Whallysson\Money\Currency\CurrencyFactory;
8
use Whallysson\Money\Formatter\FormatterInterface;
9
use Whallysson\Money\Money\MoneyFormatter;
10
use Whallysson\Money\Money\MoneyInterface;
11
12
/**
13
 * Class Money
14
 *
15
 * @author Whallysson Avelino <[email protected]>
16
 * @package Whallysson\Money
17
 */
18
class Money implements MoneyInterface
19
{
20
    /** @var string|int */
21
    private $value;
22
23
    /** @var bool */
24
    private bool $isDecimal;
25
26
    /** @var FormatterInterface */
27
    private FormatterInterface $formatter;
28
29
    public function __construct()
30
    {
31
        ini_set('precision', '16');
32
        bcscale(8);
33
        $this->formatter = new MoneyFormatter();
34
    }
35
36
    /**
37
     * @inheritDoc
38
     */
39
    public function amount($value): MoneyInterface
40
    {
41
        if (is_string($value)) {
42
            $value = str_replace(['R$', '$', '€', ' ', ','], ['', '', '', '', '.'], $value);
43
        }
44
45
        if (!is_numeric($value)) {
46
            throw new \InvalidArgumentException("Invalid number format");
47
        }
48
49
        $value = (string)$value;
50
        $this->isDecimal = (strpos($value, '.') !== false);
51
        $this->value = $value;
52
53
        return $this;
54
    }
55
56
    public function int(): int
57
    {
58
        if (!$this->isDecimal) {
59
            return (int)$this->value;
60
        }
61
62
        $parts = explode('.', (string)$this->value);
63
        $integers = $parts[0];
64
        $decimals = str_pad(substr($parts[1] . '00', 0, 2), 2, '0');
65
66
        return (int)($integers . $decimals);
67
    }
68
69
    public function decimal(int $precision = 2): string
70
    {
71
        if ($this->isDecimal) {
72
            return bcscale($precision) !== 0 ? bcdiv($this->value, '1', $precision) : $this->value;
73
        }
74
75
        $value = str_pad((string)$this->value, 3, '0', STR_PAD_LEFT);
76
        $decimals = substr($value, -2);
77
        $integers = substr($value, 0, -2);
78
        $result = $integers . '.' . $decimals;
79
80
        return bcscale($precision) !== 0 ? bcdiv($result, '1', $precision) : $result;
81
    }
82
83
    /**
84
     * @param string $operation
85
     * @param MoneyInterface|int|float|string $value
86
     * @return self
87
     */
88
    private function calculate(string $operation, $value): self
89
    {
90
        $value1 = $this->decimal();
91
92
        if ($value instanceof MoneyInterface) {
93
            $value2 = $value->decimal();
94
        } else {
95
            $value2 = (new self())->amount($value)->decimal();
96
        }
97
98
        $result = match($operation, [
0 ignored issues
show
A parse error occurred: Syntax error, unexpected ',' on line 98 at column 34
Loading history...
99
            'add' => bcadd($value1, $value2),
100
            'sub' => bcsub($value1, $value2),
101
            'mul' => bcmul($value1, $value2),
102
            'div' => bcdiv($value1, $value2)
103
        ], function () {
104
            throw new \InvalidArgumentException("Invalid operation");
105
        });
106
107
        $this->value = $result;
108
        $this->isDecimal = true;
109
110
        return $this;
111
    }
112
113
    /**
114
     * @inheritDoc
115
     */
116
    public function add($value): MoneyInterface
117
    {
118
        return $this->calculate('add', $value);
119
    }
120
121
    /**
122
     * @inheritDoc
123
     */
124
    public function sub($value): MoneyInterface
125
    {
126
        return $this->calculate('sub', $value);
127
    }
128
129
    /**
130
     * @inheritDoc
131
     */
132
    public function mul($value): MoneyInterface
133
    {
134
        return $this->calculate('mul', $value);
135
    }
136
137
    /**
138
     * @inheritDoc
139
     */
140
    public function div($value): MoneyInterface
141
    {
142
        return $this->calculate('div', $value);
143
    }
144
145
    /**
146
     * @param MoneyInterface|int|float|string $value
147
     * @return int
148
     */
149
    public function compare($value): int
150
    {
151
        $value1 = $this->decimal();
152
153
        if ($value instanceof MoneyInterface) {
154
            $value2 = $value->decimal();
155
        } else {
156
            $value2 = (new self())->amount($value)->decimal();
157
        }
158
159
        return bccomp($value1, $value2, 2);
160
    }
161
162
    /**
163
     * @inheritDoc
164
     */
165
    public function equals($value): bool
166
    {
167
        return $this->compare($value) === 0;
168
    }
169
170
    /**
171
     * @inheritDoc
172
     */
173
    public function greaterThan($value): bool
174
    {
175
        return $this->compare($value) === 1;
176
    }
177
178
    /**
179
     * @inheritDoc
180
     */
181
    public function lessThan($value): bool
182
    {
183
        return $this->compare($value) === -1;
184
    }
185
186
    /**
187
     * @param string $currency
188
     * @param bool $showSymbol
189
     * @param bool $showThousandsSeparator
190
     * @return string
191
     */
192
    public function format(
193
        string $currency = 'BRL',
194
        bool $showSymbol = true,
195
        bool $showThousandsSeparator = true
196
    ): string {
197
        $currency = CurrencyFactory::get($currency);
198
199
        return $this->formatter->format(
200
            $this->decimal(),
201
            $currency,
202
            $showSymbol,
203
            $showThousandsSeparator
204
        );
205
    }
206
207
    /**
208
     * @param int|float|string $value
209
     * @return MoneyInterface
210
     */
211
    public static function of($value): MoneyInterface
212
    {
213
        return (new self())->amount($value);
214
    }
215
}
216