PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.27.0
GiveWP – Donation Plugin and Fundraising Platform v2.27.0
4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / vendor / moneyphp / money / src / Exchange / IndirectExchange.php
give / vendor / moneyphp / money / src / Exchange Last commit date
ExchangerExchange.php 4 years ago FixedExchange.php 4 years ago IndirectExchange.php 4 years ago ReversedCurrenciesExchange.php 4 years ago SwapExchange.php 4 years ago
IndirectExchange.php
208 lines
1 <?php
2
3 namespace Money\Exchange;
4
5 use Money\Calculator;
6 use Money\Calculator\BcMathCalculator;
7 use Money\Calculator\GmpCalculator;
8 use Money\Calculator\PhpCalculator;
9 use Money\Currencies;
10 use Money\Currency;
11 use Money\CurrencyPair;
12 use Money\Exception\UnresolvableCurrencyPairException;
13 use Money\Exchange;
14
15 /**
16 * Provides a way to get an exchange rate through a minimal set of intermediate conversions.
17 *
18 * @author Michael Cordingley <Michael.Cordingley@gmail.com>
19 */
20 final class IndirectExchange implements Exchange
21 {
22 /**
23 * @var Calculator
24 */
25 private static $calculator;
26
27 /**
28 * @var array
29 */
30 private static $calculators = [
31 BcMathCalculator::class,
32 GmpCalculator::class,
33 PhpCalculator::class,
34 ];
35
36 /**
37 * @var Currencies
38 */
39 private $currencies;
40
41 /**
42 * @var Exchange
43 */
44 private $exchange;
45
46 /**
47 * @param Exchange $exchange
48 * @param Currencies $currencies
49 */
50 public function __construct(Exchange $exchange, Currencies $currencies)
51 {
52 $this->exchange = $exchange;
53 $this->currencies = $currencies;
54 }
55
56 /**
57 * @param string $calculator
58 */
59 public static function registerCalculator($calculator)
60 {
61 if (is_a($calculator, Calculator::class, true) === false) {
62 throw new \InvalidArgumentException('Calculator must implement '.Calculator::class);
63 }
64
65 array_unshift(self::$calculators, $calculator);
66 }
67
68 /**
69 * {@inheritdoc}
70 */
71 public function quote(Currency $baseCurrency, Currency $counterCurrency)
72 {
73 try {
74 return $this->exchange->quote($baseCurrency, $counterCurrency);
75 } catch (UnresolvableCurrencyPairException $exception) {
76 $rate = array_reduce($this->getConversions($baseCurrency, $counterCurrency), function ($carry, CurrencyPair $pair) {
77 return static::getCalculator()->multiply($carry, $pair->getConversionRatio());
78 }, '1.0');
79
80 return new CurrencyPair($baseCurrency, $counterCurrency, $rate);
81 }
82 }
83
84 /**
85 * @param Currency $baseCurrency
86 * @param Currency $counterCurrency
87 *
88 * @return CurrencyPair[]
89 *
90 * @throws UnresolvableCurrencyPairException
91 */
92 private function getConversions(Currency $baseCurrency, Currency $counterCurrency)
93 {
94 $startNode = $this->initializeNode($baseCurrency);
95 $startNode->discovered = true;
96
97 $nodes = [$baseCurrency->getCode() => $startNode];
98
99 $frontier = new \SplQueue();
100 $frontier->enqueue($startNode);
101
102 while ($frontier->count()) {
103 /** @var \stdClass $currentNode */
104 $currentNode = $frontier->dequeue();
105
106 /** @var Currency $currentCurrency */
107 $currentCurrency = $currentNode->currency;
108
109 if ($currentCurrency->equals($counterCurrency)) {
110 return $this->reconstructConversionChain($nodes, $currentNode);
111 }
112
113 /** @var Currency $candidateCurrency */
114 foreach ($this->currencies as $candidateCurrency) {
115 if (!isset($nodes[$candidateCurrency->getCode()])) {
116 $nodes[$candidateCurrency->getCode()] = $this->initializeNode($candidateCurrency);
117 }
118
119 /** @var \stdClass $node */
120 $node = $nodes[$candidateCurrency->getCode()];
121
122 if (!$node->discovered) {
123 try {
124 // Check if the candidate is a neighbor. This will throw an exception if it isn't.
125 $this->exchange->quote($currentCurrency, $candidateCurrency);
126
127 $node->discovered = true;
128 $node->parent = $currentNode;
129
130 $frontier->enqueue($node);
131 } catch (UnresolvableCurrencyPairException $exception) {
132 // Not a neighbor. Move on.
133 }
134 }
135 }
136 }
137
138 throw UnresolvableCurrencyPairException::createFromCurrencies($baseCurrency, $counterCurrency);
139 }
140
141 /**
142 * @param Currency $currency
143 *
144 * @return \stdClass
145 */
146 private function initializeNode(Currency $currency)
147 {
148 $node = new \stdClass();
149
150 $node->currency = $currency;
151 $node->discovered = false;
152 $node->parent = null;
153
154 return $node;
155 }
156
157 /**
158 * @param array $currencies
159 * @param \stdClass $goalNode
160 *
161 * @return CurrencyPair[]
162 */
163 private function reconstructConversionChain(array $currencies, \stdClass $goalNode)
164 {
165 $current = $goalNode;
166 $conversions = [];
167
168 while ($current->parent) {
169 $previous = $currencies[$current->parent->currency->getCode()];
170 $conversions[] = $this->exchange->quote($previous->currency, $current->currency);
171 $current = $previous;
172 }
173
174 return array_reverse($conversions);
175 }
176
177 /**
178 * @return Calculator
179 */
180 private function getCalculator()
181 {
182 if (null === self::$calculator) {
183 self::$calculator = self::initializeCalculator();
184 }
185
186 return self::$calculator;
187 }
188
189 /**
190 * @return Calculator
191 *
192 * @throws \RuntimeException If cannot find calculator for money calculations
193 */
194 private static function initializeCalculator()
195 {
196 $calculators = self::$calculators;
197
198 foreach ($calculators as $calculator) {
199 /** @var Calculator $calculator */
200 if ($calculator::supported()) {
201 return new $calculator();
202 }
203 }
204
205 throw new \RuntimeException('Cannot find calculator for money calculations');
206 }
207 }
208