Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
87.50% |
14 / 16 |
CRAP | |
97.26% |
71 / 73 |
| Number | |
0.00% |
0 / 1 |
|
87.50% |
14 / 16 |
41 | |
97.26% |
71 / 73 |
| __construct | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
| isDecimal | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| isInteger | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| isHalf | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| isCurrentEven | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| isCloserToNext | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
|||
| __toString | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
| fromString | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
| fromFloat | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
|||
| isNegative | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getIntegerPart | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getFractionalPart | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getIntegerRoundingMultiplier | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
| parseIntegerPart | |
100.00% |
1 / 1 |
10 | |
100.00% |
16 / 16 |
|||
| parseFractionalPart | |
100.00% |
1 / 1 |
4 | |
100.00% |
9 / 9 |
|||
| roundMoneyValue | |
100.00% |
1 / 1 |
7 | |
100.00% |
17 / 17 |
|||
| <?php | |
| namespace Money; | |
| /** | |
| * Represents a numeric value. | |
| * | |
| * @author Frederik Bosch <f.bosch@genkgo.nl> | |
| */ | |
| final class Number | |
| { | |
| /** | |
| * @var string | |
| */ | |
| private $integerPart; | |
| /** | |
| * @var string | |
| */ | |
| private $fractionalPart; | |
| /** | |
| * @var array | |
| */ | |
| private static $numbers = [0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; | |
| /** | |
| * @param string $integerPart | |
| * @param string $fractionalPart | |
| */ | |
| public function __construct($integerPart, $fractionalPart = '') | |
| { | |
| if ('' === $integerPart && '' === $fractionalPart) { | |
| throw new \InvalidArgumentException('Empty number is invalid'); | |
| } | |
| $this->integerPart = $this->parseIntegerPart((string) $integerPart); | |
| $this->fractionalPart = $this->parseFractionalPart((string) $fractionalPart); | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function isDecimal() | |
| { | |
| return $this->fractionalPart !== ''; | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function isInteger() | |
| { | |
| return $this->fractionalPart === ''; | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function isHalf() | |
| { | |
| return $this->fractionalPart === '5'; | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function isCurrentEven() | |
| { | |
| $lastIntegerPartNumber = $this->integerPart[strlen($this->integerPart) - 1]; | |
| return $lastIntegerPartNumber % 2 === 0; | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function isCloserToNext() | |
| { | |
| if ($this->fractionalPart === '') { | |
| return false; | |
| } | |
| return $this->fractionalPart[0] >= 5; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function __toString() | |
| { | |
| if ($this->fractionalPart === '') { | |
| return $this->integerPart; | |
| } | |
| return $this->integerPart.'.'.$this->fractionalPart; | |
| } | |
| /** | |
| * @param $number | |
| * | |
| * @return self | |
| */ | |
| public static function fromString($number) | |
| { | |
| $decimalSeparatorPosition = strpos($number, '.'); | |
| if ($decimalSeparatorPosition === false) { | |
| return new self($number, ''); | |
| } | |
| return new self( | |
| substr($number, 0, $decimalSeparatorPosition), | |
| rtrim(substr($number, $decimalSeparatorPosition + 1), '0') | |
| ); | |
| } | |
| /** | |
| * @param float $floatingPoint | |
| * | |
| * @return Number | |
| */ | |
| public static function fromFloat($floatingPoint) | |
| { | |
| if (is_float($floatingPoint) === false) { | |
| throw new \InvalidArgumentException('Floating point expected'); | |
| } | |
| return self::fromString(sprintf('%.8F', $floatingPoint)); | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function isNegative() | |
| { | |
| return $this->integerPart[0] === '-'; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function getIntegerPart() | |
| { | |
| return $this->integerPart; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function getFractionalPart() | |
| { | |
| return $this->fractionalPart; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function getIntegerRoundingMultiplier() | |
| { | |
| if ($this->integerPart[0] === '-') { | |
| return '-1'; | |
| } | |
| return '1'; | |
| } | |
| /** | |
| * @param string $number | |
| * | |
| * @return string | |
| */ | |
| private static function parseIntegerPart($number) | |
| { | |
| if ('' === $number || '0' === $number) { | |
| return '0'; | |
| } | |
| if ('-' === $number) { | |
| return '-0'; | |
| } | |
| $nonZero = false; | |
| for ($position = 0, $characters = strlen($number); $position < $characters; ++$position) { | |
| $digit = $number[$position]; | |
| if (!isset(static::$numbers[$digit]) && !(0 === $position && '-' === $digit)) { | |
| throw new \InvalidArgumentException( | |
| sprintf('Invalid integer part %s. Invalid digit %2 found', $number, $digit) | |
| ); | |
| } | |
| if (false === $nonZero && '0' === $digit) { | |
| throw new \InvalidArgumentException( | |
| 'Leading zeros are not allowed' | |
| ); | |
| } | |
| $nonZero = true; | |
| } | |
| return $number; | |
| } | |
| /** | |
| * @param string $number | |
| * | |
| * @return string | |
| */ | |
| private static function parseFractionalPart($number) | |
| { | |
| if ('' === $number) { | |
| return $number; | |
| } | |
| for ($position = 0, $characters = strlen($number); $position < $characters; ++$position) { | |
| $digit = $number[$position]; | |
| if (!isset(static::$numbers[$digit])) { | |
| throw new \InvalidArgumentException( | |
| 'Invalid fractional part '.$number.'. Invalid digit '.$digit.' found' | |
| ); | |
| } | |
| } | |
| return $number; | |
| } | |
| /** | |
| * @param string $moneyValue | |
| * @param int $targetDigits | |
| * @param int $havingDigits | |
| * | |
| * @return string | |
| */ | |
| public static function roundMoneyValue($moneyValue, $targetDigits, $havingDigits) | |
| { | |
| $valueLength = strlen($moneyValue); | |
| $shouldRound = $targetDigits < $havingDigits && $valueLength - $havingDigits + $targetDigits > 0; | |
| if ($shouldRound && $moneyValue[$valueLength - $havingDigits + $targetDigits] >= 5) { | |
| $position = $valueLength - $havingDigits + $targetDigits; | |
| $addend = 1; | |
| while ($position > 0) { | |
| $newValue = (string) ((int) $moneyValue[$position - 1] + $addend); | |
| if ($newValue >= 10) { | |
| $moneyValue[$position - 1] = $newValue[1]; | |
| $addend = $newValue[0]; | |
| --$position; | |
| if ($position === 0) { | |
| $moneyValue = $addend.$moneyValue; | |
| } | |
| } else { | |
| $moneyValue[$position - 1] = $newValue[0]; | |
| break; | |
| } | |
| } | |
| } | |
| return $moneyValue; | |
| } | |
| } |