Passed
Push — master ( 1da82a...7c3e69 )
by Alec
04:39
created

Money   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 183
ccs 52
cts 52
cp 1
rs 10
c 0
b 0
f 0
wmc 21

16 Methods

Rating   Name   Duplication   Size   Complexity  
A assertSameCurrency() 0 4 2
A negative() 0 4 1
A allocate() 0 4 1
A __construct() 0 5 1
A jsonSerialize() 0 5 1
A assertAmount() 0 9 3
A newInstance() 0 3 1
A isSameCurrency() 0 3 1
A setAmount() 0 3 1
A getCurrency() 0 3 1
A absolute() 0 4 1
A getAmount() 0 3 1
A allocateTo() 0 7 2
A setCurrency() 0 3 1
A compare() 0 5 1
A assertOperand() 0 7 2
1
<?php
2
/**
3
 * User: alec
4
 * Date: 05.11.18
5
 * Time: 23:51
6
 */
7
8
namespace AlecRabbit\Money;
9
10
use AlecRabbit\Money\CalculatorFactory as Factory;
11
use AlecRabbit\Money\Contracts\MoneyInterface;
12
13
/**
14
 * Money Value Object.
15
 *
16
 * @author Mathias Verraes
17
 */
18
class Money implements MoneyInterface, \JsonSerializable
19
{
20
    use MoneyFactory,
21
        MoneyFunctions;
22
23
    /** @var string */
24
    protected $amount;
25
26
    /** @var Currency */
27
    protected $currency;
28
29
    /**
30
     * @param null|int|float|string $amount
31
     * @param Currency $currency
32
     *
33
     * @throws \InvalidArgumentException If amount is not numeric
34
     */
35 137
    public function __construct($amount, Currency $currency)
36
    {
37 137
        $this->setAmount($this->assertAmount($amount));
38 135
        $this->setCurrency($currency);
39 135
        $this->calculator = Factory::getCalculator();
40 135
    }
41
42
    /**
43
     * @param int|float|string $amount
44
     * @return string
45
     */
46 137
    private function assertAmount($amount): string
47
    {
48 137
        if (null === $amount) {
0 ignored issues
show
introduced by
The condition null === $amount is always false.
Loading history...
49 3
            $amount = 0;
50
        }
51 137
        if (!\is_numeric($amount)) {
52 2
            throw new \InvalidArgumentException('Amount must be int|float|string');
53
        }
54 135
        return (string)$amount;
55
    }
56
57
    /**
58
     * @param string $amount
59
     */
60 135
    private function setAmount(string $amount): void
61
    {
62 135
        $this->amount = trim_zeros($amount);
63 135
    }
64
65
    /**
66
     * @param Currency $currency
67
     */
68 135
    private function setCurrency(Currency $currency): void
69
    {
70 135
        $this->currency = $currency;
71 135
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 7
    public function compare(Money $other): int
77
    {
78 7
        $this->assertSameCurrency($other);
79
80 7
        return $this->calculator->compare($this->amount, $other->getAmount());
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 42
    protected function assertSameCurrency(Money $other): void
87
    {
88 42
        if (!$this->isSameCurrency($other)) {
89 3
            throw new \InvalidArgumentException('Currencies must be identical.');
90
        }
91 42
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 50
    public function isSameCurrency(Money $other): bool
97
    {
98 50
        return $this->currency->equals($other->currency);
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104 124
    public function getAmount(): string
105
    {
106 124
        return $this->amount;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 76
    public function getCurrency(): Currency
113
    {
114 76
        return $this->currency;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     *
120
     * @return array
121
     */
122 1
    public function jsonSerialize(): array
123
    {
124
        return [
125 1
            'amount' => $this->amount,
126 1
            'currency' => $this->currency,
127
        ];
128
    }
129
130
    /**
131
     * Allocate the money among N targets.
132
     *
133
     * @param int $n
134
     *
135
     * @param int|null $precision
136
     * @return Money[]
137
     *
138
     */
139 7
    public function allocateTo(int $n, ?int $precision = null): array
140
    {
141 7
        if ($n <= 0) {
142 2
            throw new \InvalidArgumentException('Number to allocateTo must be greater than zero.');
143
        }
144
145 5
        return $this->allocate(array_fill(0, $n, 1), $precision);
146
    }
147
148
    /**
149
     * Allocate the money according to a list of ratios.
150
     *
151
     * @param array $ratios
152
     *
153
     * @param int|null $precision
154
     * @return Money[]
155
     */
156 37
    public function allocate(array $ratios, ?int $precision = null): array
157
    {
158
        return
159 37
            (new AllocationCalculator($this))->compute($ratios, $precision);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165 25
    protected function assertOperand($operand): void
166
    {
167 25
        if (!\is_numeric($operand)) {
168 10
            throw new \InvalidArgumentException(
169 10
                sprintf(
170 10
                    'Operand should be a numeric value, "%s" given.',
171 10
                    typeOf($operand)
172
                )
173
            );
174
        }
175 15
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180 33
    protected function newInstance($amount): Money
181
    {
182 33
        return new Money($amount, $this->currency);
183
    }
184
185
    /**
186
     * @return Money
187
     */
188 7
    public function absolute(): Money
189
    {
190
        return
191 7
            $this->newInstance($this->calculator->absolute($this->getAmount()));
192
    }
193
194
    /**
195
     * @return Money
196
     */
197 8
    public function negative(): Money
198
    {
199
        return
200 8
            $this->newInstance(0)->subtract($this);
201
    }
202
203
}
204