Passed
Push — master ( 54e3cc...a9003e )
by Dmitry
14:11
created

MultipliedMoney   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 16
eloc 26
c 1
b 0
f 0
dl 0
loc 94
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getAmount() 0 3 1
A isWhole() 0 4 1
A isFloat() 0 3 1
A setDecimalMoneyParser() 0 3 1
A getCurrency() 0 3 1
A money() 0 3 1
A getMoneyParser() 0 7 2
A __construct() 0 4 1
A multiplier() 0 3 1
A calculateMultiplierToInteger() 0 8 2
A create() 0 17 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace hiqdev\php\billing\Money;
6
7
use Laminas\Code\Reflection\Exception\InvalidArgumentException;
0 ignored issues
show
Bug introduced by
The type Laminas\Code\Reflection\...nvalidArgumentException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Money\Currencies\ISOCurrencies;
9
use Money\Currency;
10
use Money\Money;
11
use Money\MoneyParser;
12
use Money\Parser\DecimalMoneyParser;
13
14
/**
15
 * Class MultipliedMoney a wrapper around the Money class and provides
16
 * a way to work with sub-cent prices.
17
 *
18
 * For example, if you have a price of $0.001, it will be represented as
19
 * 1 USD with a multiplier of 1000. After you do some calculations with this
20
 * price, you can convert it back to the decimal representation by dividing
21
 * the amount by the multiplier.
22
 *
23
 * By default, the MultipliedMoney uses the DecimalMoneyParser to parse the
24
 * amount. You can set your own parser by calling the setDecimalMoneyParser.
25
 *
26
 * @author Dmytro Naumenko <[email protected]>
27
 */
28
final class MultipliedMoney
29
{
30
    private function __construct(
31
        private readonly Money $money,
32
        private readonly int $multiplier = 1
33
    ) {
34
    }
35
36
    /**
37
     * @param numeric-string $amount
0 ignored issues
show
Documentation Bug introduced by
The doc comment numeric-string at position 0 could not be parsed: Unknown type name 'numeric-string' at position 0 in numeric-string.
Loading history...
38
     * @param string $currencyCode
39
     */
40
    public static function create(string $amount, string $currencyCode): MultipliedMoney
41
    {
42
        if (!is_numeric($amount)) {
43
            throw new InvalidArgumentException('Amount of the MultipliedMoney must be numeric');
44
        }
45
46
        $currency = new Currency($currencyCode);
47
        $parser = self::getMoneyParser();
48
49
        if (!self::isFloat($amount) || self::isWhole($amount)) {
50
            return new self($parser->parse($amount, $currency), 1);
51
        }
52
53
        $multiplier = self::calculateMultiplierToInteger($amount);
54
        return new self(
55
            $parser->parse((string)($amount * $multiplier), $currency),
56
            $multiplier
57
        );
58
    }
59
60
    public function money(): Money
61
    {
62
        return $this->money;
63
    }
64
65
    public function multiplier(): int
66
    {
67
        return $this->multiplier;
68
    }
69
70
    public function getCurrency(): Currency
71
    {
72
        return $this->money->getCurrency();
73
    }
74
75
    public function getAmount(): string
76
    {
77
        return $this->money->getAmount();
78
    }
79
80
    /**
81
     * @param numeric-string $amount
0 ignored issues
show
Documentation Bug introduced by
The doc comment numeric-string at position 0 could not be parsed: Unknown type name 'numeric-string' at position 0 in numeric-string.
Loading history...
82
     */
83
    private static function calculateMultiplierToInteger(string $amount): int
84
    {
85
        if (self::isWhole($amount)) {
86
            return 1;
87
        }
88
89
        [$integer, $fraction] = explode('.', $amount, 2);
90
        return (int)('1' . implode(array_fill(0, strlen($fraction), 0)));
91
    }
92
93
    /**
94
     * @param numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment numeric-string at position 0 could not be parsed: Unknown type name 'numeric-string' at position 0 in numeric-string.
Loading history...
95
     */
96
    private static function isWhole(string $number): bool
97
    {
98
        /** @noinspection PhpWrongStringConcatenationInspection */
99
        return is_int($number + 0);
100
    }
101
102
    /**
103
     * @param numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment numeric-string at position 0 could not be parsed: Unknown type name 'numeric-string' at position 0 in numeric-string.
Loading history...
104
     */
105
    private static function isFloat(string $number): bool
106
    {
107
        return str_contains($number, '.');
108
    }
109
110
    private static MoneyParser $moneyParser;
111
    public static function setDecimalMoneyParser(MoneyParser $moneyParser): void
112
    {
113
        self::$moneyParser = $moneyParser;
114
    }
115
    private static function getMoneyParser(): MoneyParser
116
    {
117
        if (!isset(self::$moneyParser)) {
118
            self::setDecimalMoneyParser(new DecimalMoneyParser(new ISOCurrencies()));
119
        }
120
121
        return self::$moneyParser;
122
    }
123
124
}
125