Asset::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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