Test Setup Failed
Push — develop ( c0da3f...b920e1 )
by Remco
05:14
created

Money::get_number()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 1
cp 0
crap 2
rs 10
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 1.2.5
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
	 * Calculator.
40
	 *
41
	 * @var Calculator|null
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Money\Calculator was not found. Did you mean Calculator? If so, make sure to prefix the type with \.
Loading history...
42
	 */
43
	private static $calculator;
44
45
	/**
46
	 * Calculators.
47
	 *
48
	 * @var array<int, string>
49
	 */
50
	private static $calculators = array(
51
		BcMathCalculator::class,
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Money\BcMathCalculator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
52
		PhpCalculator::class,
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Money\PhpCalculator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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 95
	public function __construct( $value = 0, $currency = 'EUR' ) {
62 95
		$this->set_value( $value );
63 95
		$this->set_currency( $currency );
64 95
	}
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
				(string) $this->currency->get_symbol(),
111 14
				number_format_i18n( $this->get_value(), $number_decimals ),
0 ignored issues
show
Bug introduced by
$this->get_value() of type string is incompatible with the type double expected by parameter $number of number_format_i18n(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
				number_format_i18n( /** @scrutinizer ignore-type */ $this->get_value(), $number_decimals ),
Loading history...
112 14
				strval( $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
				(string) $this->currency->get_symbol(),
154 7
				number_format( $this->amount->get_value(), $this->get_currency()->get_number_decimals(), '.', '' ),
0 ignored issues
show
Bug introduced by
$this->amount->get_value() of type string is incompatible with the type double expected by parameter $num of number_format(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

154
				number_format( /** @scrutinizer ignore-type */ $this->amount->get_value(), $this->get_currency()->get_number_decimals(), '.', '' ),
Loading history...
155 7
				strval( $alphabetic_code )
156
			);
157
		}
158
159
		return number_format( $this->get_value(), 2, '.', '' );
160
	}
161
162
	/**
163
	 * Get value.
164
	 *
165
	 * @return string Amount value.
166
	 */
167 95
	public function get_value() {
168 95
		return $this->amount->get_value();
169
	}
170
171
	/**
172
	 * Get number.
173
	 *
174
	 * @return Number
175
	 */
176
	public function get_number() {
177
		return $this->amount;
178
	}
179
180
	/**
181
	 * Get amount.
182
	 *
183
	 * @deprecated 1.2.0
184
	 * @return float Amount value.
185
	 */
186
	public function get_amount() {
187
		_deprecated_function( __METHOD__, '1.2.0', 'Money::get_value()' );
188
189
		return $this->get_value();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->get_value() returns the type string which is incompatible with the documented return type double.
Loading history...
190 1
	}
191 1
192
	/**
193
	 * Get cents.
194
	 *
195
	 * @return float
196
	 *
197
	 * @deprecated 1.2.2 Use `Money::get_minor_units()` instead.
198
	 */
199
	public function get_cents() {
200
		return (float) $this->get_minor_units();
201
	}
202
203
	/**
204
	 * Get amount in minor units.
205
	 *
206
	 * Examples for value 10:
207 21
	 *   JPY 0 decimals: 10
208 21
	 *   EUR 2 decimals: 1000
209
	 *   BHD 3 decimals: 10000
210
	 *   NLG 4 decimals: 100000
211 21
	 *
212
	 * @since 1.2.1
213 21
	 *
214
	 * @return int
215 21
	 */
216
	public function get_minor_units() {
217
		$minor_units = $this->amount->multiply( Number::from_mixed( \pow( 10, $this->currency->get_number_decimals() ) ) );
218
219
		return (int) $minor_units->get_value();
220
	}
221
222
	/**
223
	 * Set value.
224 95
	 *
225 95
	 * @param mixed $value Amount value.
226 95
	 * @return void
227
	 */
228
	final public function set_value( $value ) {
229
		if ( ! $value instanceof Number ) {
230
			$value = Number::from_mixed( $value );
231
		}
232
233
		$this->amount = $value;
234
	}
235
236
	/**
237
	 * Set amount.
238
	 *
239
	 * @deprecated 1.2.0
240
	 * @param mixed $value Amount value.
241
	 * @return void
242
	 */
243
	public function set_amount( $value ) {
244
		_deprecated_function( __METHOD__, '1.2.0', 'Money::set_value()' );
245
246 7
		$this->set_value( $value );
247 7
	}
248
249
	/**
250
	 * Get currency.
251
	 *
252
	 * @return Currency
253
	 */
254
	public function get_currency() {
255
		return $this->currency;
256 95
	}
257 95
258
	/**
259
	 * Set currency.
260
	 *
261
	 * @param string|Currency $currency Currency.
262
	 * @return void
263 95
	 */
264 95
	final public function set_currency( $currency ) {
265
		if ( $currency instanceof Currency ) {
266
			$this->currency = $currency;
267
268
			return;
269
		}
270
271
		$this->currency = Currency::get_instance( $currency );
272
	}
273
274
	/**
275
	 * Create a string representation of this money object.
276
	 *
277
	 * @return string
278
	 */
279
	public function __toString() {
280
		return $this->format_i18n();
281
	}
282
283 1
	/**
284 1
	 * JSON serialize.
285
	 * 
286 1
	 * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php
287
	 * @return object
288
	 */
289 1
	public function jsonSerialize() {
290 1
		$properties = array(
291
			'value' => $money->get_value(),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $money seems to be never defined.
Loading history...
292 1
		);
293
294 1
		if ( null !== $this->currency ) {
295
			$properties['currency'] = $this->currency->jsonSerialize();
296 1
		}
297
298 1
		$object = (object) $properties;
299
300
		return $object;
301
	}
302
303
	/**
304
	 * Returns a new Money object that represents
305
	 * the sum of this and an other Money object.
306
	 *
307
	 * @param Money $addend Addend.
308
	 *
309
	 * @return Money
310
	 */
311
	public function add( Money $addend ) {
312
		$result = $this->amount->add( $addend->get_number() );
313
314
		return new self( $result, $this->currency );
315
	}
316
317
	/**
318
	 * Returns a new Money object that represents
319
	 * the difference of this and an other Money object.
320
	 *
321
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L235-L255
322
	 *
323
	 * @param Money $subtrahend Subtrahend.
324
	 *
325
	 * @return Money
326
	 */
327
	public function subtract( Money $subtrahend ) {
328
		$result = $this->amount->subtract( $subtrahend->get_number() );
329
330
		return new self( $result, $this->currency );
331
	}
332
333
	/**
334
	 * Returns a new Money object that represents
335
	 * the multiplied value of this Money object.
336
	 *
337
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L299-L316
338
	 *
339
	 * @param int|float|string $multiplier Multiplier.
340
	 *
341
	 * @return Money
342
	 */
343
	public function multiply( $multiplier ) {
344
		$result = $this->amount->multiply( $multiplier->get_number() );
345
346
		return new self( $result, $this->currency );
347
	}
348
349
	/**
350
	 * Returns a new Money object that represents
351
	 * the divided value of this Money object.
352
	 *
353
	 * @link https://github.com/moneyphp/money/blob/v3.2.1/src/Money.php#L318-L341
354
	 *
355
	 * @param int|float|string $divisor Divisor.
356
	 *
357
	 * @return Money
358
	 */
359
	public function divide( $divisor ) {
360
		$result = $this->amount->divide( $divisor->get_number() );
361
362
		return new self( $result, $this->currency );
363
	}
364
365
	/**
366
	 * Absolute.
367
	 *
368
	 * @link https://github.com/moneyphp/money/blob/v4.0.1/src/Money.php#L411-L417
369
	 * @return Money
370
	 */
371
	public function absolute() {
372
		return new self(
373
			$this->amount->absolute(),
0 ignored issues
show
Bug introduced by
The method absolute() does not exist on Pronamic\WordPress\Number\Number. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

373
			$this->amount->/** @scrutinizer ignore-call */ 
374
                  absolute(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
374
			$this->currency
375
		);
376
	}
377
378
	/**
379
	 * Initialize calculator.
380
	 *
381
	 * @return Calculator
382
	 *
383
	 * @throws \RuntimeException If cannot find calculator for money calculations.
384
	 */
385
	private static function initialize_calculator() {
386
		$calculator_classes = self::$calculators;
387
388
		foreach ( $calculator_classes as $calculator_class ) {
389
			if ( $calculator_class::supported() ) {
390
				$calculator = new $calculator_class();
391
392
				if ( $calculator instanceof Calculator ) {
393
					return $calculator;
394
				}
395
			}
396 1
		}
397 1
398
		throw new \RuntimeException( 'Cannot find calculator for money calculations' );
399 1
	}
400 1
401 1
	/**
402
	 * Get calculator.
403 1
	 *
404 1
	 * @return Calculator
405
	 */
406
	protected function get_calculator() {
407
		if ( null === self::$calculator ) {
408
			self::$calculator = self::initialize_calculator();
409
		}
410
411
		return self::$calculator;
412
	}
413
}
414