Failed Conditions
Push — develop ( 0f9467...fd59d6 )
by Reüel
03:32
created

Money::initialize_calculator()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

Changes 0
Metric Value
cc 4
eloc 7
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 14
rs 10
ccs 7
cts 8
cp 0.875
crap 4.0312
1
<?php
2
/**
3
 * Money
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2019 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Money
9
 */
10
11
namespace Pronamic\WordPress\Money;
12
13
use Pronamic\WordPress\Money\Calculator\BcMathCalculator;
14
use Pronamic\WordPress\Money\Calculator\PhpCalculator;
15
16
/**
17
 * Money
18
 *
19
 * @author Remco Tolsma
20
 * @version 1.2.1
21
 * @since   1.0.0
22
 */
23
class Money {
24
	/**
25
	 * Amount value.
26
	 *
27
	 * @var float
28
	 */
29
	private $value;
30
31
	/**
32
	 * Currency.
33
	 *
34
	 * @var Currency
35
	 */
36
	private $currency;
37
38
	/**
39
	 * Calculator.
40
	 *
41
	 * @var Calculator|null
42
	 */
43
	private static $calculator;
44
45
	/**
46
	 * Calculators.
47
	 *
48
	 * @var array
49
	 */
50
	private static $calculators = [
51
		BcMathCalculator::class,
52
		PhpCalculator::class,
53
	];
54
55
	/**
56
	 * Construct and initialize money object.
57
	 *
58
	 * @param string|int|float $value    Amount value.
59
	 * @param Currency|string  $currency Currency.
60
	 */
61 74
	public function __construct( $value = 0, $currency = 'EUR' ) {
62 74
		$this->set_value( $value );
63 74
		$this->set_currency( $currency );
64 74
	}
65
66
	/**
67
	 * Get default format.
68
	 *
69
	 * @return string
70
	 */
71 17
	public static function get_default_format() {
72
		/* translators: 1: currency symbol, 2: amount value, 3: currency code, note: use non-breaking space! */
73 17
		$format = _x( '%1$s%2$s %3$s', 'money format', 'pronamic-money' );
74
		// Note:               ↳ Non-breaking space.
75 17
		$format = apply_filters( 'pronamic_money_default_format', $format );
76
77 17
		return $format;
78
	}
79
80
	/**
81
	 * Format i18n.
82
	 *
83
	 * @param string|null $format Format.
84
	 *
85
	 * @return string
86
	 */
87 14
	public function format_i18n( $format = null ) {
88 14
		if ( is_null( $format ) ) {
89 7
			$format = self::get_default_format();
90
		}
91
92 14
		$alphabetic_code = $this->currency->get_alphabetic_code();
93
94 14
		if ( ! empty( $alphabetic_code ) ) {
95 14
			$number_decimals = $this->currency->get_number_decimals();
96
97
			// Handle non trailing zero formatter.
98 14
			if ( false !== strpos( $format, '%2$NTZ' ) ) {
99 7
				$decimals = substr( $this->format(), ( - 1 * $number_decimals ), $number_decimals );
100
101 7
				if ( 0 === (int) $decimals ) {
102 5
					$number_decimals = 0;
103
				}
104
105 7
				$format = str_replace( '%2$NTZ', '%2$s', $format );
106
			}
107
108 14
			return sprintf(
109 14
				$format,
110 14
				strval( $this->currency->get_symbol() ),
111 14
				number_format_i18n( $this->get_value(), $number_decimals ),
112 14
				strval( $this->currency->get_alphabetic_code() )
113
			);
114
		}
115
116
		return number_format_i18n( $this->get_value(), 2 );
117
	}
118
119
	/**
120
	 * Format i18n without trailing zeros.
121
	 *
122
	 * @param string|null $format Format.
123
	 *
124
	 * @return string
125
	 */
126 7
	public function format_i18n_non_trailing_zeros( $format = null ) {
127 7
		if ( is_null( $format ) ) {
128 7
			$format = self::get_default_format();
129
		}
130
131 7
		$format = str_replace( '%2$s', '%2$NTZ', $format );
132
133 7
		return $this->format_i18n( $format );
134
	}
135
136
	/**
137
	 * Format.
138
	 *
139
	 * @param string|null $format Format.
140
	 *
141
	 * @return string
142
	 */
143 7
	public function format( $format = null ) {
144 7
		if ( is_null( $format ) ) {
145 7
			$format = '%2$s';
146
		}
147
148 7
		$alphabetic_code = $this->currency->get_alphabetic_code();
149
150 7
		if ( ! empty( $alphabetic_code ) ) {
151 7
			return sprintf(
152 7
				$format,
153 7
				strvaL( $this->currency->get_symbol() ),
154 7
				number_format( $this->get_value(), $this->get_currency()->get_number_decimals(), '.', '' ),
155 7
				strval( $this->currency->get_alphabetic_code() )
156
			);
157
		}
158
159
		return number_format( $this->get_value(), 2, '.', '' );
160
	}
161
162
	/**
163
	 * Get value.
164
	 *
165
	 * @return float Amount value.
166
	 */
167 74
	public function get_value() {
168 74
		return $this->value;
169
	}
170
171
	/**
172
	 * Get amount.
173
	 *
174
	 * @deprecated 1.2.0
175
	 * @return float Amount value.
176
	 */
177
	public function get_amount() {
178
		_deprecated_function( __METHOD__, '1.2.0', 'Money::get_value()' );
179
180
		return $this->get_value();
181
	}
182
183
	/**
184
	 * Get cents.
185
	 *
186
	 * @return float
187
	 *
188
	 * @deprecated 1.2.2 Use `Money::get_minor_units()` instead.
189
	 */
190 1
	public function get_cents() {
191 1
		return (float) $this->get_minor_units();
192
	}
193
194
	/**
195
	 * Get amount in minor units.
196
	 *
197
	 * Examples for value 10:
198
	 *   JPY 0 decimals: 10
199
	 *   EUR 2 decimals: 1000
200
	 *   BHD 3 decimals: 10000
201
	 *   NLG 4 decimals: 100000
202
	 *
203
	 * @since 1.2.1
204
	 *
205
	 * @return int
206
	 */
207 21
	public function get_minor_units() {
208 21
		$calculator = $this->get_calculator();
209
210 21
		$minor_units = $calculator->multiply( strval( $this->get_value() ), pow( 10, $this->currency->get_number_decimals() ) );
211
212 21
		return (int) $minor_units;
213
	}
214
215
	/**
216
	 * Set value.
217
	 *
218
	 * @param mixed $value Amount value.
219
	 * @return void
220
	 */
221 74
	public function set_value( $value ) {
222 74
		$this->value = floatval( $value );
223 74
	}
224
225
	/**
226
	 * Set amount.
227
	 *
228
	 * @deprecated 1.2.0
229
	 * @param mixed $value Amount value.
230
	 * @return void
231
	 */
232
	public function set_amount( $value ) {
233
		_deprecated_function( __METHOD__, '1.2.0', 'Money::set_value()' );
234
235
		$this->set_value( $value );
236
	}
237
238
	/**
239
	 * Get currency.
240
	 *
241
	 * @return Currency
242
	 */
243 8
	public function get_currency() {
244 8
		return $this->currency;
245
	}
246
247
	/**
248
	 * Set currency.
249
	 *
250
	 * @param string|Currency $currency Currency.
251
	 * @return void
252
	 */
253 74
	public function set_currency( $currency ) {
254 74
		if ( $currency instanceof Currency ) {
255 1
			$this->currency = $currency;
256
257 1
			return;
258
		}
259
260 74
		$this->currency = Currency::get_instance( $currency );
261 74
	}
262
263
	/**
264
	 * Create a string representation of this money object.
265
	 *
266
	 * @return string
267
	 */
268
	public function __toString() {
269
		return $this->format_i18n();
270
	}
271
272
	/**
273
	 * Returns a new Money object that represents
274
	 * the sum of this and an other Money object.
275
	 *
276
	 * @param Money $addend Addend.
277
	 *
278
	 * @return Money
279
	 */
280 1
	public function add( Money $addend ) {
281 1
		$value = $this->get_value();
282
283 1
		$calculator = $this->get_calculator();
284
285 1
		$value = $calculator->add( strval( $value ), strval( $addend->get_value() ) );
286
287 1
		return new self( $value, $this->get_currency() );
288
	}
289
290
	/**
291
	 * Returns a new Money object that represents
292
	 * the difference of this and an other Money object.
293
	 *
294
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L235-L255
295
	 *
296
	 * @param Money $subtrahend Subtrahend.
297
	 *
298
	 * @return Money
299
	 */
300
	public function subtract( Money $subtrahend ) {
301
		$value = $this->get_value();
302
303
		$calculator = $this->get_calculator();
304
305
		$value = $calculator->subtract( strval( $value ), strval( $subtrahend->get_value() ) );
306
307
		return new self( $value, $this->get_currency() );
308
	}
309
310
	/**
311
	 * Returns a new Money object that represents
312
	 * the multiplied value of this Money object.
313
	 *
314
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L299-L316
315
	 *
316
	 * @param int|float|string $multiplier Multiplier.
317
	 *
318
	 * @return Money
319
	 */
320
	public function multiply( $multiplier ) {
321
		$value = $this->get_value();
322
323
		$calculator = $this->get_calculator();
324
325
		$value = $calculator->multiply( strval( $value ), $multiplier );
326
327
		return new self( $value, $this->get_currency() );
328
	}
329
330
	/**
331
	 * Returns a new Money object that represents
332
	 * the divided value of this Money object.
333
	 *
334
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L318-L341
335
	 *
336
	 * @param int|float|string $divisor Divisor.
337
	 *
338
	 * @return Money
339
	 */
340
	public function divide( $divisor ) {
341
		$value = $this->get_value();
342
343
		$calculator = $this->get_calculator();
344
345
		$value = $calculator->divide( strval( $value ), $divisor );
346
347
		if ( null === $value ) {
348
			$value = $this->get_value();
349
		}
350
351
		return new self( $value, $this->get_currency() );
352
	}
353
354
	/**
355
	 * Initialize calculator.
356
	 *
357
	 * @return Calculator
358
	 *
359
	 * @throws \RuntimeException If cannot find calculator for money calculations.
360
	 */
361 1
	private static function initialize_calculator() {
362 1
		$calculator_classes = self::$calculators;
363
364 1
		foreach ( $calculator_classes as $calculator_class ) {
365 1
			if ( $calculator_class::supported() ) {
366 1
				$calculator = new $calculator_class();
367
368 1
				if ( $calculator instanceof Calculator ) {
369 1
					return $calculator;
370
				}
371
			}
372
		}
373
374
		throw new \RuntimeException( 'Cannot find calculator for money calculations' );
375
	}
376
377
	/**
378
	 * Get calculator.
379
	 *
380
	 * @return Calculator
381
	 */
382 22
	protected function get_calculator() {
383 22
		if ( null === self::$calculator ) {
384 1
			self::$calculator = self::initialize_calculator();
385
		}
386
387 22
		return self::$calculator;
388
	}
389
}
390