-
-
Notifications
You must be signed in to change notification settings - Fork 102
/
CurrencyConverter.php
100 lines (85 loc) · 3.6 KB
/
CurrencyConverter.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?php
declare(strict_types=1);
namespace Brick\Money;
use Brick\Money\Context\DefaultContext;
use Brick\Money\Exception\CurrencyConversionException;
use Brick\Math\BigRational;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\RoundingMode;
/**
* Converts monies into different currencies, using an exchange rate provider.
*
* @todo Now that this class provides methods to convert to both Money and RationalMoney, it makes little sense to
* provide the context in the constructor, as this only applies to convert() and not convertToRational().
* This should probably be a parameter to convert().
*/
final class CurrencyConverter
{
/**
* The exchange rate provider.
*
* @var ExchangeRateProvider
*/
private $exchangeRateProvider;
/**
* The context of the monies created by this currency converter.
*
* @var Context
*/
private $context;
/**
* @param ExchangeRateProvider $exchangeRateProvider The exchange rate provider.
* @param Context|null $context A context to create the monies in, or null to use the default.
* The context only applies to convert(), not convertToRational().
*/
public function __construct(ExchangeRateProvider $exchangeRateProvider, Context $context = null)
{
if ($context === null) {
$context = new DefaultContext();
}
$this->exchangeRateProvider = $exchangeRateProvider;
$this->context = $context;
}
/**
* Converts the given money to the given currency.
*
* @param MoneyContainer $moneyContainer The Money, RationalMoney or MoneyBag to convert.
* @param Currency|string|int $currency The Currency instance, ISO currency code or ISO numeric currency code.
* @param int $roundingMode The rounding mode, if necessary.
*
* @return Money
*
* @throws CurrencyConversionException If the exchange rate is not available.
* @throws RoundingNecessaryException If rounding is necessary and RoundingMode::UNNECESSARY is used.
*/
public function convert(MoneyContainer $moneyContainer, $currency, int $roundingMode = RoundingMode::UNNECESSARY) : Money
{
return $this->convertToRational($moneyContainer, $currency)->to($this->context, $roundingMode);
}
/**
* Converts the given money to the given currency, and returns the result as a RationalMoney with no rounding.
*
* @param MoneyContainer $moneyContainer The Money, RationalMoney or MoneyBag to convert.
* @param Currency|string|int $currency The Currency instance, ISO currency code or ISO numeric currency code.
*
* @return RationalMoney
*
* @throws CurrencyConversionException If the exchange rate is not available.
*/
public function convertToRational(MoneyContainer $moneyContainer, $currency) : RationalMoney
{
if (! $currency instanceof Currency) {
$currency = Currency::of($currency);
}
$currencyCode = $currency->getCurrencyCode();
$total = BigRational::zero();
foreach ($moneyContainer->getAmounts() as $sourceCurrencyCode => $amount) {
if ($sourceCurrencyCode !== $currencyCode) {
$exchangeRate = $this->exchangeRateProvider->getExchangeRate($sourceCurrencyCode, $currencyCode);
$amount = $amount->toBigRational()->multipliedBy($exchangeRate);
}
$total = $total->plus($amount);
}
return new RationalMoney($total, $currency);
}
}