25 private static $calculator;
30 private static $calculators = [
31 BcMathCalculator::class,
52 $this->exchange = $exchange;
53 $this->currencies = $currencies;
61 if (is_a($calculator, Calculator::class,
true) ===
false) {
62 throw new \InvalidArgumentException(
'Calculator must implement '.Calculator::class);
65 array_unshift(self::$calculators, $calculator);
74 return $this->exchange->quote($baseCurrency, $counterCurrency);
76 $rate = array_reduce($this->getConversions($baseCurrency, $counterCurrency),
function ($carry,
CurrencyPair $pair) {
80 return new CurrencyPair($baseCurrency, $counterCurrency, $rate);
92 private function getConversions(
Currency $baseCurrency,
Currency $counterCurrency)
94 $startNode = $this->initializeNode($baseCurrency);
95 $startNode->discovered =
true;
97 $nodes = [$baseCurrency->getCode() => $startNode];
99 $frontier = new \SplQueue();
100 $frontier->enqueue($startNode);
102 while ($frontier->count()) {
104 $currentNode = $frontier->dequeue();
107 $currentCurrency = $currentNode->currency;
109 if ($currentCurrency->equals($counterCurrency)) {
110 return $this->reconstructConversionChain($nodes, $currentNode);
114 foreach ($this->currencies as $candidateCurrency) {
115 if (!isset($nodes[$candidateCurrency->getCode()])) {
116 $nodes[$candidateCurrency->getCode()] = $this->initializeNode($candidateCurrency);
120 $node = $nodes[$candidateCurrency->getCode()];
122 if (!$node->discovered) {
125 $this->exchange->quote($currentCurrency, $candidateCurrency);
127 $node->discovered =
true;
128 $node->parent = $currentNode;
130 $frontier->enqueue($node);
131 }
catch (UnresolvableCurrencyPairException $exception) {
146 private function initializeNode(
Currency $currency)
148 $node = new \stdClass();
150 $node->currency = $currency;
151 $node->discovered =
false;
152 $node->parent =
null;
163 private function reconstructConversionChain(array $currencies, \stdClass $goalNode)
165 $current = $goalNode;
168 while ($current->parent) {
169 $previous = $currencies[$current->parent->currency->getCode()];
170 $conversions[] = $this->exchange->quote($previous->currency, $current->currency);
171 $current = $previous;
174 return array_reverse($conversions);
180 private function getCalculator()
182 if (
null === self::$calculator) {
183 self::$calculator = self::initializeCalculator();
186 return self::$calculator;
194 private static function initializeCalculator()
196 $calculators = self::$calculators;
198 foreach ($calculators as $calculator) {
200 if ($calculator::supported()) {
201 return new $calculator();
205 throw new \RuntimeException(
'Cannot find calculator for money calculations');