1 | <?php |
||
2 | /** |
||
3 | * Money |
||
4 | * |
||
5 | * @author Pronamic <[email protected]> |
||
6 | * @copyright 2005-2023 Pronamic |
||
7 | * @license GPL-3.0-or-later |
||
8 | * @package Pronamic\WordPress\Money |
||
9 | */ |
||
10 | |||
11 | namespace Pronamic\WordPress\Money; |
||
12 | |||
13 | use JsonSerializable; |
||
14 | use Pronamic\WordPress\Number\Number; |
||
15 | |||
16 | /** |
||
17 | * Money |
||
18 | * |
||
19 | * @author Remco Tolsma |
||
20 | * @version 2.0.0 |
||
21 | * @since 1.0.0 |
||
22 | */ |
||
23 | class Money implements JsonSerializable { |
||
24 | /** |
||
25 | * Number. |
||
26 | * |
||
27 | * @var Number |
||
28 | */ |
||
29 | private $amount; |
||
30 | |||
31 | /** |
||
32 | * Currency. |
||
33 | * |
||
34 | * @var Currency |
||
35 | */ |
||
36 | private $currency; |
||
37 | |||
38 | /** |
||
39 | * Construct and initialize money object. |
||
40 | * |
||
41 | * @param mixed $value Amount value. |
||
42 | * @param Currency|string $currency Currency. |
||
43 | */ |
||
44 | 101 | public function __construct( $value = 0, $currency = 'EUR' ) { |
|
45 | 101 | $this->set_value( $value ); |
|
46 | 101 | $this->set_currency( $currency ); |
|
47 | 101 | } |
|
48 | |||
49 | /** |
||
50 | * Get default format. |
||
51 | * |
||
52 | * @return string |
||
53 | */ |
||
54 | 17 | public static function get_default_format() { |
|
55 | /* translators: 1: currency symbol, 2: amount value, 3: currency code, note: use non-breaking space! */ |
||
56 | 17 | $format = _x( '%1$s%2$s %3$s', 'money format', 'pronamic-money' ); |
|
57 | // Note: ↳ Non-breaking space. |
||
58 | |||
59 | /** |
||
60 | * Filters the default money format. |
||
61 | * |
||
62 | * Default format: `%1$s%2$s %3$s` |
||
63 | * |
||
64 | * - 1: Currency symbol |
||
65 | * - 2: Amount value |
||
66 | * - 3: Currency code |
||
67 | * |
||
68 | * Note: use non-breaking space `Â ` in money formatting. |
||
69 | * |
||
70 | * @param string $format Format. |
||
71 | */ |
||
72 | 17 | $format = apply_filters( 'pronamic_money_default_format', $format ); |
|
73 | |||
74 | 17 | return $format; |
|
75 | } |
||
76 | |||
77 | /** |
||
78 | * Format i18n. |
||
79 | * |
||
80 | * @param string|null $format Format. |
||
81 | * |
||
82 | * @return string |
||
83 | */ |
||
84 | 14 | public function format_i18n( $format = null ) { |
|
85 | 14 | if ( is_null( $format ) ) { |
|
86 | 7 | $format = self::get_default_format(); |
|
87 | } |
||
88 | |||
89 | 14 | $alphabetic_code = $this->currency->get_alphabetic_code(); |
|
90 | 14 | $number_decimals = $this->currency->get_number_decimals(); |
|
91 | |||
92 | // Handle non trailing zero formatter. |
||
93 | 14 | if ( false !== \strpos( $format, '%2$NTZ' ) ) { |
|
94 | 7 | $decimals = \substr( $this->format(), ( - 1 * $number_decimals ), $number_decimals ); |
|
95 | |||
96 | 7 | if ( 0 === (int) $decimals ) { |
|
97 | 5 | $number_decimals = 0; |
|
98 | } |
||
99 | |||
100 | 7 | $format = \str_replace( '%2$NTZ', '%2$s', $format ); |
|
101 | } |
||
102 | |||
103 | 14 | return \sprintf( |
|
104 | 14 | $format, |
|
105 | 14 | (string) $this->currency->get_symbol(), |
|
106 | 14 | $this->amount->format_i18n( $number_decimals ), |
|
107 | 14 | \strval( $alphabetic_code ) |
|
108 | ); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Format i18n without trailing zeros. |
||
113 | * |
||
114 | * @param string|null $format Format. |
||
115 | * |
||
116 | * @return string |
||
117 | */ |
||
118 | 7 | public function format_i18n_non_trailing_zeros( $format = null ) { |
|
119 | 7 | if ( is_null( $format ) ) { |
|
120 | 7 | $format = self::get_default_format(); |
|
121 | } |
||
122 | |||
123 | 7 | $format = str_replace( '%2$s', '%2$NTZ', $format ); |
|
124 | |||
125 | 7 | return $this->format_i18n( $format ); |
|
126 | } |
||
127 | |||
128 | /** |
||
129 | * Format. |
||
130 | * |
||
131 | * @param string|null $format Format. |
||
132 | * |
||
133 | * @return string |
||
134 | */ |
||
135 | 7 | public function format( $format = null ) { |
|
136 | 7 | if ( is_null( $format ) ) { |
|
137 | 7 | $format = '%2$s'; |
|
138 | } |
||
139 | |||
140 | 7 | $alphabetic_code = $this->currency->get_alphabetic_code(); |
|
141 | |||
142 | 7 | return \sprintf( |
|
143 | 7 | $format, |
|
144 | 7 | (string) $this->currency->get_symbol(), |
|
145 | 7 | $this->amount->format( $this->get_currency()->get_number_decimals() ), |
|
146 | 7 | \strval( $alphabetic_code ) |
|
147 | ); |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Number format. |
||
152 | * |
||
153 | * @param int $decimals Precision of the number of decimal places. |
||
154 | * @param string|null $decimal_separator Sets the separator for the decimal point. |
||
155 | * @param string|null $thousands_separator Sets the thousands separator. |
||
156 | * @return string |
||
157 | */ |
||
158 | 6 | public function number_format( $decimals = null, $decimal_separator = '.', $thousands_separator = ',' ) { |
|
159 | 6 | if ( null === $decimals ) { |
|
160 | 6 | $decimals = $this->currency->get_number_decimals(); |
|
161 | } |
||
162 | |||
163 | 6 | return $this->amount->format( $decimals, $decimal_separator, $thousands_separator ); |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * Number format i18n. |
||
168 | * |
||
169 | * @param int $decimals Precision of the number of decimal places. |
||
170 | * @return string |
||
171 | */ |
||
172 | 1 | public function number_format_i18n( $decimals = null ) { |
|
173 | 1 | if ( null === $decimals ) { |
|
174 | 1 | $decimals = $this->currency->get_number_decimals(); |
|
175 | } |
||
176 | |||
177 | 1 | return $this->amount->format_i18n( $decimals ); |
|
178 | } |
||
179 | |||
180 | /** |
||
181 | * Get value. |
||
182 | * |
||
183 | * @return string Amount value. |
||
184 | */ |
||
185 | 62 | public function get_value() { |
|
186 | 62 | return $this->amount->get_value(); |
|
187 | } |
||
188 | |||
189 | /** |
||
190 | * Get number. |
||
191 | * |
||
192 | * @return Number |
||
193 | */ |
||
194 | 2 | public function get_number() { |
|
195 | 2 | return $this->amount; |
|
196 | } |
||
197 | |||
198 | /** |
||
199 | * Get amount in minor units. |
||
200 | * |
||
201 | * Examples for value 10: |
||
202 | * JPY 0 decimals: 10 |
||
203 | * EUR 2 decimals: 1000 |
||
204 | * BHD 3 decimals: 10000 |
||
205 | * NLG 4 decimals: 100000 |
||
206 | * |
||
207 | * @link https://en.wikipedia.org/wiki/Cent_(currency) |
||
208 | * @link https://simple.wikipedia.org/wiki/ISO_4217 |
||
209 | * @since 1.2.1 |
||
210 | * @return Number |
||
211 | */ |
||
212 | 16 | public function get_minor_units() { |
|
213 | 16 | return $this->amount->multiply( Number::from_mixed( \pow( 10, $this->currency->get_number_decimals() ) ) ); |
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
214 | } |
||
215 | |||
216 | /** |
||
217 | * Set value. |
||
218 | * |
||
219 | * @param mixed $value Amount value. |
||
220 | * @return void |
||
221 | */ |
||
222 | 101 | final public function set_value( $value ) { |
|
223 | 101 | $this->amount = Number::from_mixed( $value ); |
|
224 | 101 | } |
|
225 | |||
226 | /** |
||
227 | * Get currency. |
||
228 | * |
||
229 | * @return Currency |
||
230 | */ |
||
231 | 10 | public function get_currency() { |
|
232 | 10 | return $this->currency; |
|
233 | } |
||
234 | |||
235 | /** |
||
236 | * Set currency. |
||
237 | * |
||
238 | * @param string|Currency $currency Currency. |
||
239 | * @return void |
||
240 | */ |
||
241 | 101 | final public function set_currency( $currency ) { |
|
242 | 101 | if ( ! $currency instanceof Currency ) { |
|
243 | 101 | $currency = Currency::get_instance( $currency ); |
|
244 | } |
||
245 | |||
246 | 101 | $this->currency = $currency; |
|
247 | 101 | } |
|
248 | |||
249 | /** |
||
250 | * Create a string representation of this money object. |
||
251 | * |
||
252 | * @return string |
||
253 | */ |
||
254 | 1 | public function __toString() { |
|
255 | 1 | return \sprintf( |
|
256 | 1 | '%s %s', |
|
257 | 1 | $this->currency->get_alphabetic_code(), |
|
258 | 1 | $this->amount->format( $this->get_currency()->get_number_decimals() ) |
|
259 | ); |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * JSON serialize. |
||
264 | * |
||
265 | * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php |
||
266 | * @return object |
||
267 | */ |
||
268 | 2 | public function jsonSerialize(): object { |
|
269 | return (object) [ |
||
270 | 2 | 'value' => $this->amount->get_value(), |
|
271 | 2 | 'currency' => $this->currency->jsonSerialize(), |
|
272 | ]; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Returns a new Money object that represents |
||
277 | * the sum of this and an other Money object. |
||
278 | * |
||
279 | * @param Money $addend Addend. |
||
280 | * @return Money |
||
281 | */ |
||
282 | 1 | public function add( Money $addend ) { |
|
283 | 1 | $result = $this->amount->add( $addend->get_number() ); |
|
284 | |||
285 | 1 | return new self( $result, $this->currency ); |
|
286 | } |
||
287 | |||
288 | /** |
||
289 | * Returns a new Money object that represents |
||
290 | * the difference of this and an other Money object. |
||
291 | * |
||
292 | * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L235-L255 |
||
293 | * @param Money $subtrahend Subtrahend. |
||
294 | * @return Money |
||
295 | */ |
||
296 | 1 | public function subtract( Money $subtrahend ) { |
|
297 | 1 | $result = $this->amount->subtract( $subtrahend->get_number() ); |
|
298 | |||
299 | 1 | return new self( $result, $this->currency ); |
|
300 | } |
||
301 | |||
302 | /** |
||
303 | * Returns a new Money object that represents |
||
304 | * the multiplied value of this Money object. |
||
305 | * |
||
306 | * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L299-L316 |
||
307 | * @param mixed $multiplier Multiplier. |
||
308 | * @return Money |
||
309 | */ |
||
310 | 1 | public function multiply( $multiplier ) { |
|
311 | 1 | $multiplier = Number::from_mixed( $multiplier ); |
|
312 | |||
313 | 1 | $result = $this->amount->multiply( $multiplier ); |
|
314 | |||
315 | 1 | return new self( $result, $this->currency ); |
|
316 | } |
||
317 | |||
318 | /** |
||
319 | * Returns a new Money object that represents |
||
320 | * the divided value of this Money object. |
||
321 | * |
||
322 | * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L318-L341 |
||
323 | * @param mixed $divisor Divisor. |
||
324 | * @return Money |
||
325 | */ |
||
326 | 1 | public function divide( $divisor ) { |
|
327 | 1 | $divisor = Number::from_mixed( $divisor ); |
|
328 | |||
329 | 1 | $result = $this->amount->divide( $divisor ); |
|
330 | |||
331 | 1 | return new self( $result, $this->currency ); |
|
332 | } |
||
333 | |||
334 | /** |
||
335 | * Absolute. |
||
336 | * |
||
337 | * @link https://github.com/moneyphp/money/blob/v4.0.1/src/Money.php#L411-L417 |
||
338 | * @return Money |
||
339 | */ |
||
340 | 1 | public function absolute() { |
|
341 | 1 | return new self( |
|
342 | 1 | $this->amount->absolute(), |
|
343 | 1 | $this->currency |
|
344 | ); |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Negative. |
||
349 | * |
||
350 | * @link https://github.com/pronamic/wp-money/issues/5 |
||
351 | * @return Money |
||
352 | */ |
||
353 | public function negative() { |
||
354 | return new self( |
||
355 | $this->amount->negative(), |
||
356 | $this->currency |
||
357 | ); |
||
358 | } |
||
359 | } |
||
360 |