Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
Decimal
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
10 / 10
22
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNumberOfDecimals
n/a
0 / 0
n/a
0 / 0
0
 getRegularExpression
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromNative
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 toNative
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 jsonSerialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 subtract
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 multiply
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 divide
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace Apie\Core\ValueObjects;
4
5use Apie\Core\ValueObjects\Exceptions\InvalidStringForValueObjectException;
6use Apie\Core\ValueObjects\Interfaces\HasRegexValueObjectInterface;
7use Apie\Core\ValueObjects\Interfaces\ValueObjectInterface;
8
9abstract class Decimal implements ValueObjectInterface, HasRegexValueObjectInterface
10{
11    final protected function __construct(
12        private int $integerPart,
13        private string $decimalPart,
14    ) {
15    }
16
17    final public function __toString(): string
18    {
19        return $this->integerPart . '.' . $this->decimalPart;
20    }
21
22    abstract public static function getNumberOfDecimals(): int;
23
24    final public static function getRegularExpression(): string
25    {
26        return '/^-?([0-9]|[1-9][0-9]*)\.[0-9]{' .static::getNumberOfDecimals() . '}$/';
27    }
28
29    final public static function fromNative(mixed $input): static
30    {
31        $decimals = static::getNumberOfDecimals();
32        if (is_int($input) || is_float($input)) {
33            $formatted = number_format($input, $decimals, '.', '');
34            [$int, $dec] = explode('.', $formatted);
35            return new static((int)$int, $dec);
36        }
37        $string = trim(Utils::toString($input));
38        // we do not use getRegularExpression here, because we are more lenient on fromNative.
39        if (preg_match(
40            '/^(?<int>-?([0-9]|[1-9][0-9]*))\.(?<part>[0-9]{0,' . $decimals . '})[0-9]*$/',
41            $string,
42            $matches
43        )) {
44            return new static((int)$matches['int'], str_pad($matches['part'], $decimals, '0', STR_PAD_RIGHT));
45        }
46
47        throw new InvalidStringForValueObjectException($input, new \ReflectionClass(static::class));
48    }
49
50    final public function toNative(): string
51    {
52        return $this->__toString();
53    }
54
55    final public function jsonSerialize(): string
56    {
57        return $this->__toString();
58    }
59
60    final public function add(int|float|self ...$values): static
61    {
62        $sum = (float)$this->__toString();
63        foreach ($values as $value) {
64            $sum += (float) ($value instanceof self ? $value->__toString() : $value);
65        }
66        return static::fromNative($sum);
67    }
68
69    final public function subtract(int|float|self ...$values): static
70    {
71        $result = (float)$this->__toString();
72        foreach ($values as $value) {
73            $result -= (float) ($value instanceof self ? $value->__toString() : $value);
74        }
75        return static::fromNative($result);
76    }
77
78    final public function multiply(int|float|self ...$values): static
79    {
80        $result = (float)$this->__toString();
81        foreach ($values as $value) {
82            $result *= (float) ($value instanceof self ? $value->__toString() : $value);
83        }
84        return static::fromNative($result);
85    }
86
87    final public function divide(int|float|self ...$values): static
88    {
89        $result = (float)$this->__toString();
90        foreach ($values as $value) {
91            $divisor = (float) ($value instanceof self ? $value->__toString() : $value);
92            if ($divisor == 0.0) {
93                throw new \DivisionByZeroError("Division by zero");
94            }
95            $result /= $divisor;
96        }
97        return static::fromNative($result);
98    }
99}