Completed
Push — master ( d62204...7d07ad )
by BENOIT
07:40
created

AverageExchangeRateProvider::withProviders()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace BenTools\Currency\Provider;
4
5
use BenTools\Currency\Model\CurrencyInterface;
6
use BenTools\Currency\Model\ExchangeRate;
7
use BenTools\Currency\Model\ExchangeRateFactoryInterface;
8
use BenTools\Currency\Model\ExchangeRateInterface;
9
use BenTools\Currency\Model\ExchangeRateNotFoundException;
10
use BenTools\Currency\Model\NativeExchangeRateFactory;
11
use DateTimeInterface;
12
13
final class AverageExchangeRateProvider implements ExchangeRateProviderInterface
14
{
15
    /**
16
     * @var ExchangeRateProviderInterface[]
17
     */
18
    private $exchangeRateProviders = [];
19
20
    /**
21
     * @var float
22
     */
23
    private $tolerance;
24
25
    /**
26
     * @var ExchangeRateFactoryInterface
27
     */
28
    private $exchangeRateFactory;
29
30
    /**
31
     * AverageExchangeRateProvider constructor.
32
     * @param float|null                        $tolerance
33
     * @param ExchangeRateFactoryInterface|null $exchangeRateFactory
34
     */
35
    public function __construct(float $tolerance = null, ExchangeRateFactoryInterface $exchangeRateFactory = null)
36
    {
37
        $this->tolerance = $tolerance;
38
        $this->exchangeRateFactory = $exchangeRateFactory ?? new NativeExchangeRateFactory();
39
    }
40
41
    /**
42
     * @param ExchangeRateProviderInterface[] ...$exchangeRateProviders
43
     * @return AverageExchangeRateProvider
44
     */
45
    public function withProviders(ExchangeRateProviderInterface ...$exchangeRateProviders): self
46
    {
47
        $clone = clone $this;
48
        foreach ($exchangeRateProviders as $exchangeRateProvider) {
49
            $clone->exchangeRateProviders[] = $exchangeRateProvider;
50
        }
51
        return $clone;
52
    }
53
54
    /**
55
     * @inheritDoc
56
     */
57
    public function getExchangeRate(CurrencyInterface $sourceCurrency, CurrencyInterface $targetCurrency, DateTimeInterface $date = null): ExchangeRateInterface
58
    {
59
        $exchangeRates = [];
60
        foreach ($this->exchangeRateProviders as $e => $exchangeRateProvider) {
61
            $exchangeRates[$e] = $er = $exchangeRateProvider->getExchangeRate($sourceCurrency, $targetCurrency, $date);
0 ignored issues
show
Unused Code introduced by
$er is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
62
        }
63
64
        if (!$exchangeRates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $exchangeRates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
65
            throw new ExchangeRateNotFoundException($sourceCurrency, $targetCurrency);
66
        }
67
68
        $averageRatio = self::getAverageRatio(...$exchangeRates);
69
70
        if (null !== $this->tolerance) {
71
            $this->validate(...$exchangeRates);
72
        }
73
74
        return $this->exchangeRateFactory->create($sourceCurrency, $targetCurrency, $averageRatio);
75
    }
76
77
    /**
78
     * @param ExchangeRateInterface[] ...$exchangeRates
79
     * @throws \RuntimeException
80
     */
81
    private function validate(ExchangeRateInterface ...$exchangeRates): void
82
    {
83
        $ratios = array_map(function (ExchangeRate $exchangeRate) {
84
            return $exchangeRate->getRatio();
85
        }, $exchangeRates);
86
87
        $min = min($ratios);
88
        $max = max($ratios);
89
        $diff = $max - $min;
90
91
        if ($diff > $this->tolerance) {
92
            throw new \RuntimeException(sprintf('Tolerance fault: %s difference between minimum and maximum ratio, %s allowed.', $diff, $this->tolerance));
93
        }
94
    }
95
96
    /**
97
     * @param array $array
0 ignored issues
show
Bug introduced by
There is no parameter named $array. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
98
     * @return float
99
     */
100
    private static function getAverageRatio(ExchangeRateInterface ...$exchangeRates): float
101
    {
102
        return array_sum(
103
            array_map(
104
                function (ExchangeRateInterface $exchangeRate): float {
105
                        return $exchangeRate->getRatio();
106
                },
107
                $exchangeRates
108
            )
109
        ) / count($exchangeRates);
110
    }
111
112
    /**
113
     * @param float|null                        $tolerance
114
     * @param ExchangeRateFactoryInterface|null $exchangeRateFactory
115
     * @return AverageExchangeRateProvider
116
     */
117
    public static function create(float $tolerance = null, ExchangeRateFactoryInterface $exchangeRateFactory = null): self
118
    {
119
        return new self($tolerance, $exchangeRateFactory);
120
    }
121
}
122