Completed
Pull Request — master (#11)
by Tim
05:23
created

Euro::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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