Completed
Pull Request — master (#6)
by Jeroen De
02:55
created

Euro::centsFromString()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
crap 3
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Euro;
6
7
use InvalidArgumentException;
8
9
/**
10
 * @licence GNU GPL v2+
11
 * @author Jeroen De Dauw < [email protected] >
12
 */
13
final class Euro {
14
15
	private static $DECIMAL_COUNT = 2;
16
	private static $CENTS_PER_EURO = 100;
17
18
	private $cents;
19
20
	/**
21
	 * @param int $cents
22
	 * @throws InvalidArgumentException
23
	 */
24 52
	private function __construct( int $cents ) {
25 52
		if ( $cents < 0 ) {
26 5
			throw new InvalidArgumentException( 'Amount needs to be positive' );
27
		}
28
29 47
		$this->cents = $cents;
30 47
	}
31
32
	/**
33
	 * @param int $cents
34
	 * @return self
35
	 * @throws InvalidArgumentException
36
	 */
37 30
	public static function newFromCents( int $cents ): self {
38 30
		return new self( $cents );
39
	}
40
41
	/**
42
	 * Constructs a Euro object from a string representation such as "13.37".
43
	 *
44
	 * This method takes into account the errors that can arise from floating
45
	 * point number usage. Amounts with too many decimals are rounded to the
46
	 * nearest whole euro cent amount.
47
	 *
48
	 * @param string $euroAmount
49
	 * @return self
50
	 * @throws InvalidArgumentException
51
	 */
52 15
	public static function newFromString( string $euroAmount ): self {
53 15
		if ( !is_numeric( $euroAmount ) ) {
54 3
			throw new InvalidArgumentException( 'Not a number' );
55
		}
56
57 12
		list( $euros, $cents ) = explode( '.', $euroAmount, 2 );
58
59 12
		$euros = (int)$euros;
60 12
		$cents = self::centsFromString( $cents ?? '0' );
61
62 12
		return new self( $euros * self::$CENTS_PER_EURO + $cents );
63
	}
64
65 12
	private static function centsFromString( string $cents ): int {
66 12
		$cents = str_pad( $cents, 2, '0' );
67
68 12
		if ( strlen( $cents ) <= 2 ) {
69 10
			return (int)$cents;
70
		}
71
72 2
		$centsInt = (int)substr( $cents, 0, 2 );
73
74 2
		if ( (int)$cents[2] >= 5 ) {
75 1
			$centsInt++;
76
		}
77
78 2
		return $centsInt;
79
	}
80
81
	/**
82
	 * This method takes into account the errors that can arise from floating
83
	 * point number usage. Amounts with too many decimals are rounded to the
84
	 * nearest whole euro cent amount.
85
	 *
86
	 * @param float $euroAmount
87
	 * @return self
88
	 * @throws InvalidArgumentException
89
	 */
90 6
	public static function newFromFloat( float $euroAmount ) {
91 6
		return new self( intval(
92 6
			round(
93 6
				round( $euroAmount, self::$DECIMAL_COUNT ) * self::$CENTS_PER_EURO,
94
				0
95
			)
96
		) );
97
	}
98
99
	/**
100
	 * @param int $euroAmount
101
	 * @return self
102
	 * @throws InvalidArgumentException
103
	 */
104 5
	public static function newFromInt( int $euroAmount ) {
105 5
		return new self( $euroAmount * self::$CENTS_PER_EURO );
106
	}
107
108 32
	public function getEuroCents(): int {
109 32
		return $this->cents;
110
	}
111
112 10
	public function getEuroFloat(): float {
113 10
		return $this->cents / self::$CENTS_PER_EURO;
114
	}
115
116
	/**
117
	 * Returns the euro amount as string with two decimals always present in format "42.00".
118
	 */
119 6
	public function getEuroString(): string {
120 6
		return number_format( $this->getEuroFloat(), self::$DECIMAL_COUNT, '.', '' );
121
	}
122
123 10
	public function equals( Euro $euro ): bool {
124 10
		return $this->cents === $euro->cents;
125
	}
126
127
}
128