1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Kanunak\Money; |
4
|
|
|
|
5
|
|
|
class Money |
6
|
|
|
{ |
7
|
|
|
const NUMBER_OF_DECIMALS = 4; |
8
|
|
|
|
9
|
|
|
/** @var Currency */ |
10
|
|
|
private $currency; |
11
|
|
|
|
12
|
|
|
/** @var int */ |
13
|
|
|
private $value; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @param Currency $currency |
17
|
|
|
* @param int $value |
18
|
|
|
*/ |
19
|
|
|
public function __construct(Currency $currency, $value) |
20
|
|
|
{ |
21
|
|
|
$this->validateValue($value); |
22
|
|
|
$this->currency = $currency; |
23
|
|
|
$this->value = $value; |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @return Currency |
28
|
|
|
*/ |
29
|
|
|
public function currency() |
30
|
|
|
{ |
31
|
|
|
return $this->currency; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @return int |
36
|
|
|
*/ |
37
|
|
|
public function value() |
38
|
|
|
{ |
39
|
|
|
return $this->value; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @return int |
44
|
|
|
*/ |
45
|
|
|
public function decimals() |
46
|
|
|
{ |
47
|
|
|
return self::NUMBER_OF_DECIMALS; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param Money $moneyToAdd |
52
|
|
|
* @return Money |
53
|
|
|
* @throw \InvalidArgumentException |
54
|
|
|
*/ |
55
|
|
View Code Duplication |
public function add(Money $moneyToAdd) |
|
|
|
|
56
|
|
|
{ |
57
|
|
|
if (!$this->isSameCurrency($moneyToAdd)) { |
58
|
|
|
throw new \InvalidArgumentException('Currencies does not match'); |
59
|
|
|
} |
60
|
|
|
return new Money($this->currency(), $this->value() + $moneyToAdd->value()); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @param Money $moneyToSubtract |
65
|
|
|
* @return Money |
66
|
|
|
* @throw \InvalidArgumentException |
67
|
|
|
*/ |
68
|
|
View Code Duplication |
public function subtract(Money $moneyToSubtract) |
|
|
|
|
69
|
|
|
{ |
70
|
|
|
if (!$this->isSameCurrency($moneyToSubtract)) { |
71
|
|
|
throw new \InvalidArgumentException('Currencies does not match'); |
72
|
|
|
} |
73
|
|
|
return new Money($this->currency(), $this->value() - $moneyToSubtract->value()); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @param Money $moneyToCompare |
78
|
|
|
* @return bool |
79
|
|
|
* @throw \InvalidArgumentException |
80
|
|
|
*/ |
81
|
|
|
public function equals(Money $moneyToCompare) |
82
|
|
|
{ |
83
|
|
|
if ($this->isSameCurrency($moneyToCompare)) { |
84
|
|
|
return $this->value() === $moneyToCompare->value(); |
85
|
|
|
} |
86
|
|
|
return false; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @param CurrencyPair $currencyPair |
91
|
|
|
* @return Money |
92
|
|
|
* @throw \InvalidArgumentException |
93
|
|
|
*/ |
94
|
|
|
public function exchange(CurrencyPair $currencyPair) |
95
|
|
|
{ |
96
|
|
|
if (!$this->isSameCurrencyInCurrencyPair($currencyPair)) { |
97
|
|
|
throw new \InvalidArgumentException('CurrencyFrom in the CurrencyPair does not match the money currency'); |
98
|
|
|
} |
99
|
|
|
return new Money($currencyPair->currencyTo(), (int)($this->value() * $currencyPair->ratio())); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @param CurrencyPair $currencyPair |
104
|
|
|
* @return bool |
105
|
|
|
*/ |
106
|
|
|
private function isSameCurrencyInCurrencyPair(CurrencyPair $currencyPair) |
107
|
|
|
{ |
108
|
|
|
return ($this->currency()->equals($currencyPair->currencyFrom())); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @param Money $money |
113
|
|
|
* @return bool |
114
|
|
|
*/ |
115
|
|
|
private function isSameCurrency(Money $money) |
116
|
|
|
{ |
117
|
|
|
return ($this->currency()->equals($money->currency())); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @param $value |
122
|
|
|
* @thrown \InvalidArgumentException |
123
|
|
|
*/ |
124
|
|
|
private function validateValue($value) |
125
|
|
|
{ |
126
|
|
|
if (!is_integer($value)) { |
127
|
|
|
throw new \InvalidArgumentException('Provided value is not an integer: '.$value); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.