Failed Conditions
Push — develop ( 47852a...24a14f )
by Remco
07:52 queued 03:25
created

src/Money.php (1 issue)

1
<?php
2
/**
3
 * Money
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2021 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 90
	public function __construct( $value = 0, $currency = 'EUR' ) {
45 90
		$this->set_value( $value );
46 90
		$this->set_currency( $currency );
47 90
	}
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 17
		$format = apply_filters( 'pronamic_money_default_format', $format );
59
60 17
		return $format;
61
	}
62
63
	/**
64
	 * Format i18n.
65
	 *
66
	 * @param string|null $format Format.
67
	 *
68
	 * @return string
69
	 */
70 14
	public function format_i18n( $format = null ) {
71 14
		if ( is_null( $format ) ) {
72 7
			$format = self::get_default_format();
73
		}
74
75 14
		$alphabetic_code = $this->currency->get_alphabetic_code();
76
77 14
		if ( ! empty( $alphabetic_code ) ) {
78 14
			$number_decimals = $this->currency->get_number_decimals();
79
80
			// Handle non trailing zero formatter.
81 14
			if ( false !== strpos( $format, '%2$NTZ' ) ) {
82 7
				$decimals = substr( $this->format(), ( - 1 * $number_decimals ), $number_decimals );
83
84 7
				if ( 0 === (int) $decimals ) {
85 5
					$number_decimals = 0;
86
				}
87
88 7
				$format = str_replace( '%2$NTZ', '%2$s', $format );
89
			}
90
91 14
			return sprintf(
92 14
				$format,
93 14
				(string) $this->currency->get_symbol(),
94 14
				$this->amount->format_i18n( $number_decimals ),
95 14
				strval( $alphabetic_code )
96
			);
97
		}
98
99
		return $this->amount->format_i18n( 2 );
100
	}
101
102
	/**
103
	 * Format i18n without trailing zeros.
104
	 *
105
	 * @param string|null $format Format.
106
	 *
107
	 * @return string
108
	 */
109 7
	public function format_i18n_non_trailing_zeros( $format = null ) {
110 7
		if ( is_null( $format ) ) {
111 7
			$format = self::get_default_format();
112
		}
113
114 7
		$format = str_replace( '%2$s', '%2$NTZ', $format );
115
116 7
		return $this->format_i18n( $format );
117
	}
118
119
	/**
120
	 * Format.
121
	 *
122
	 * @param string|null $format Format.
123
	 *
124
	 * @return string
125
	 */
126 7
	public function format( $format = null ) {
127 7
		if ( is_null( $format ) ) {
128 7
			$format = '%2$s';
129
		}
130
131 7
		$alphabetic_code = $this->currency->get_alphabetic_code();
132
133 7
		if ( ! empty( $alphabetic_code ) ) {
134 7
			return sprintf(
135 7
				$format,
136 7
				(string) $this->currency->get_symbol(),
137 7
				$this->amount->format( $this->get_currency()->get_number_decimals() ),
138 7
				strval( $alphabetic_code )
139
			);
140
		}
141
142
		return $this->amount->format( 2 );
143
	}
144
145
	/**
146
	 * Number format.
147
	 * 
148
	 * @param int         $decimals            Precision of the number of decimal places.
149
	 * @param string|null $decimal_separator   Sets the separator for the decimal point.
150
	 * @param string|null $thousands_separator Sets the thousands separator.
151
	 * @return string
152
	 */
153
	public function number_format( $decimals = null, $decimal_separator = '.', $thousands_separator = ',' ) {
154
		if ( null === $decimals ) {
155
			$decimals = $this->currency->get_number_decimals();
156
		}
157
158
		return $this->amount->format( $decimals, $decimal_separator, $thousands_separator );
159
	}
160
161
	/**
162
	 * Number format i18n.
163
	 * 
164
	 * @param int $decimals Precision of the number of decimal places.
165
	 * @return string
166
	 */
167
	public function number_format_i18n( $decimals = null ) {
168
		if ( null === $decimals ) {
169
			$decimals = $this->currency->get_number_decimals();
170
		}
171
172
		return $this->amount->format_i18n( $decimals );
173
	}
174
175
	/**
176
	 * Get value.
177
	 *
178
	 * @return string Amount value.
179
	 */
180 60
	public function get_value() {
181 60
		return $this->amount->get_value();
182
	}
183
184
	/**
185
	 * Get number.
186
	 *
187
	 * @return Number
188
	 */
189 1
	public function get_number() {
190 1
		return $this->amount;
191
	}
192
193
	/**
194
	 * Get amount.
195
	 *
196
	 * @deprecated 1.2.0
197
	 * @return string Amount value.
198
	 */
199
	public function get_amount() {
200
		_deprecated_function( __METHOD__, '1.2.0', 'Money::get_value()' );
201
202
		return $this->get_value();
203
	}
204
205
	/**
206
	 * Get amount in minor units.
207
	 *
208
	 * Examples for value 10:
209
	 *   JPY 0 decimals: 10
210
	 *   EUR 2 decimals: 1000
211
	 *   BHD 3 decimals: 10000
212
	 *   NLG 4 decimals: 100000
213
	 *
214
	 * @link https://en.wikipedia.org/wiki/Cent_(currency)
215
	 * @link https://simple.wikipedia.org/wiki/ISO_4217
216
	 * @since 1.2.1
217
	 * @return Number
218
	 */
219 16
	public function get_minor_units() {
220 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...
221
	}
222
223
	/**
224
	 * Set value.
225
	 *
226
	 * @param mixed $value Amount value.
227
	 * @return void
228
	 */
229 90
	final public function set_value( $value ) {
230 90
		$this->amount = Number::from_mixed( $value );
231 90
	}
232
233
	/**
234
	 * Set amount.
235
	 *
236
	 * @deprecated 1.2.0
237
	 * @param mixed $value Amount value.
238
	 * @return void
239
	 */
240
	public function set_amount( $value ) {
241
		_deprecated_function( __METHOD__, '1.2.0', 'Money::set_value()' );
242
243
		$this->set_value( $value );
244
	}
245
246
	/**
247
	 * Get currency.
248
	 *
249
	 * @return Currency
250
	 */
251 7
	public function get_currency() {
252 7
		return $this->currency;
253
	}
254
255
	/**
256
	 * Set currency.
257
	 *
258
	 * @param string|Currency $currency Currency.
259
	 * @return void
260
	 */
261 90
	final public function set_currency( $currency ) {
262 90
		if ( ! $currency instanceof Currency ) {
263 90
			$currency = Currency::get_instance( $currency );
264
		}
265
266 90
		$this->currency = $currency;
267 90
	}
268
269
	/**
270
	 * Create a string representation of this money object.
271
	 *
272
	 * @return string
273
	 */
274
	public function __toString() {
275
		return $this->format_i18n();
276
	}
277
278
	/**
279
	 * JSON serialize.
280
	 *
281
	 * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php
282
	 * @return object
283
	 */
284
	public function jsonSerialize() {
285
		$properties = array(
286
			'value' => $this->amount->get_value(),
287
		);
288
289
		if ( null !== $this->currency ) {
290
			$properties['currency'] = $this->currency->jsonSerialize();
291
		}
292
293
		$object = (object) $properties;
294
295
		return $object;
296
	}
297
298
	/**
299
	 * Returns a new Money object that represents
300
	 * the sum of this and an other Money object.
301
	 *
302
	 * @param Money $addend Addend.
303
	 *
304
	 * @return Money
305
	 */
306 1
	public function add( Money $addend ) {
307 1
		$result = $this->amount->add( $addend->get_number() );
308
309 1
		return new static( $result, $this->currency );
310
	}
311
312
	/**
313
	 * Returns a new Money object that represents
314
	 * the difference of this and an other Money object.
315
	 *
316
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L235-L255
317
	 *
318
	 * @param Money $subtrahend Subtrahend.
319
	 *
320
	 * @return Money
321
	 */
322
	public function subtract( Money $subtrahend ) {
323
		$result = $this->amount->subtract( $subtrahend->get_number() );
324
325
		return new static( $result, $this->currency );
326
	}
327
328
	/**
329
	 * Returns a new Money object that represents
330
	 * the multiplied value of this Money object.
331
	 *
332
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L299-L316
333
	 *
334
	 * @param mixed $multiplier Multiplier.
335
	 *
336
	 * @return Money
337
	 */
338
	public function multiply( $multiplier ) {
339
		$multiplier = Number::from_mixed( $multiplier );
340
341
		$result = $this->amount->multiply( $multiplier );
342
343
		return new static( $result, $this->currency );
344
	}
345
346
	/**
347
	 * Returns a new Money object that represents
348
	 * the divided value of this Money object.
349
	 *
350
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L318-L341
351
	 *
352
	 * @param mixed $divisor Divisor.
353
	 *
354
	 * @return Money
355
	 */
356
	public function divide( $divisor ) {
357
		$divisor = Number::from_mixed( $divisor );
358
359
		$result = $this->amount->divide( $divisor );
360
361
		return new static( $result, $this->currency );
362
	}
363
364
	/**
365
	 * Absolute.
366
	 *
367
	 * @link https://github.com/moneyphp/money/blob/v4.0.1/src/Money.php#L411-L417
368
	 * @return Money
369
	 */
370
	public function absolute() {
371
		return new static(
372
			$this->amount->absolute(),
373
			$this->currency
374
		);
375
	}
376
}
377