Issues (1)

src/Money.php (1 issue)

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
The expression return $this->amount->mu...et_number_decimals()))) returns the type double|integer which is incompatible with the documented return type Pronamic\WordPress\Number\Number.
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