• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / money / 21942503490

12 Feb 2026 10:14AM UTC coverage: 99.411% (-0.2%) from 99.606%
21942503490

push

github

BenMorel
Disallow float inputs

2 of 2 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

506 of 509 relevant lines covered (99.41%)

33.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.92
/src/ExchangeRateProvider/PDOProvider.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Money\ExchangeRateProvider;
6

7
use Brick\Money\Exception\CurrencyConversionException;
8
use Brick\Money\ExchangeRateProvider;
9
use InvalidArgumentException;
10
use Override;
11
use PDO;
12
use PDOStatement;
13

14
use function implode;
15
use function is_float;
16
use function sprintf;
17
use function var_export;
18

19
/**
20
 * Reads exchange rates from a PDO database connection.
21
 */
22
final class PDOProvider implements ExchangeRateProvider
23
{
24
    /**
25
     * The SELECT statement.
26
     */
27
    private readonly PDOStatement $statement;
28

29
    /**
30
     * The source currency code if fixed, or null if dynamic.
31
     */
32
    private readonly ?string $sourceCurrencyCode;
33

34
    /**
35
     * The target currency code if fixed, or null if dynamic.
36
     */
37
    private readonly ?string $targetCurrencyCode;
38

39
    /**
40
     * Extra parameters set dynamically to resolve the query placeholders.
41
     */
42
    private array $parameters = [];
43

44
    /**
45
     * @throws InvalidArgumentException
46
     */
47
    public function __construct(PDO $pdo, PDOProviderConfiguration $configuration)
48
    {
49
        $conditions = [];
23✔
50

51
        if ($configuration->whereConditions !== null) {
23✔
52
            $conditions[] = sprintf('(%s)', $configuration->whereConditions);
8✔
53
        }
54

55
        $sourceCurrencyCode = null;
23✔
56
        $targetCurrencyCode = null;
23✔
57

58
        if ($configuration->sourceCurrencyCode !== null) {
23✔
59
            $sourceCurrencyCode = $configuration->sourceCurrencyCode;
5✔
60
        } elseif ($configuration->sourceCurrencyColumnName !== null) {
18✔
61
            $conditions[] = sprintf('%s = ?', $configuration->sourceCurrencyColumnName);
18✔
62
        }
63

64
        if ($configuration->targetCurrencyCode !== null) {
23✔
65
            $targetCurrencyCode = $configuration->targetCurrencyCode;
5✔
66
        } elseif ($configuration->targetCurrencyColumnName !== null) {
18✔
67
            $conditions[] = sprintf('%s = ?', $configuration->targetCurrencyColumnName);
18✔
68
        }
69

70
        $this->sourceCurrencyCode = $sourceCurrencyCode;
23✔
71
        $this->targetCurrencyCode = $targetCurrencyCode;
23✔
72

73
        $conditions = implode(' AND ', $conditions);
23✔
74

75
        $query = sprintf(
23✔
76
            'SELECT %s FROM %s WHERE %s',
23✔
77
            $configuration->exchangeRateColumnName,
23✔
78
            $configuration->tableName,
23✔
79
            $conditions,
23✔
80
        );
23✔
81

82
        $this->statement = $pdo->prepare($query);
23✔
83
    }
84

85
    /**
86
     * Sets the parameters to dynamically resolve the extra query placeholders, if any.
87
     *
88
     * This is used in conjunction with $whereConditions in the configuration class.
89
     * The number of parameters passed to this method must match the number of placeholders.
90
     */
91
    public function setParameters(mixed ...$parameters): void
92
    {
93
        $this->parameters = $parameters;
8✔
94
    }
95

96
    #[Override]
97
    public function getExchangeRate(string $sourceCurrencyCode, string $targetCurrencyCode): int|string
98
    {
99
        $parameters = $this->parameters;
23✔
100

101
        if ($this->sourceCurrencyCode === null) {
23✔
102
            $parameters[] = $sourceCurrencyCode;
18✔
103
        } elseif ($this->sourceCurrencyCode !== $sourceCurrencyCode) {
5✔
104
            $info = 'source currency must be ' . $this->sourceCurrencyCode;
2✔
105

106
            throw CurrencyConversionException::exchangeRateNotAvailable($sourceCurrencyCode, $targetCurrencyCode, $info);
2✔
107
        }
108

109
        if ($this->targetCurrencyCode === null) {
21✔
110
            $parameters[] = $targetCurrencyCode;
16✔
111
        } elseif ($this->targetCurrencyCode !== $targetCurrencyCode) {
5✔
112
            $info = 'target currency must be ' . $this->targetCurrencyCode;
2✔
113

114
            throw CurrencyConversionException::exchangeRateNotAvailable($sourceCurrencyCode, $targetCurrencyCode, $info);
2✔
115
        }
116

117
        $this->statement->execute($parameters);
19✔
118

119
        /** @var int|float|numeric-string|false $exchangeRate */
120
        $exchangeRate = $this->statement->fetchColumn();
19✔
121

122
        if ($exchangeRate === false) {
19✔
123
            if ($this->parameters !== []) {
8✔
124
                $info = [];
4✔
125
                /** @psalm-suppress MixedAssignment */
126
                foreach ($this->parameters as $parameter) {
4✔
127
                    $info[] = var_export($parameter, true);
4✔
128
                }
129
                $info = 'parameters: ' . implode(', ', $info);
4✔
130
            } else {
131
                $info = null;
4✔
132
            }
133

134
            throw CurrencyConversionException::exchangeRateNotAvailable($sourceCurrencyCode, $targetCurrencyCode, $info);
8✔
135
        }
136

137
        if (is_float($exchangeRate)) {
11✔
138
            return (string) $exchangeRate;
11✔
139
        }
140

UNCOV
141
        return $exchangeRate;
×
142
    }
143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc