|
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\GmpCalculator; |
|
15
|
|
|
use Pronamic\WordPress\Money\Calculator\PhpCalculator; |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* Money |
|
19
|
|
|
* |
|
20
|
|
|
* @author Remco Tolsma |
|
21
|
|
|
* @version 1.2.1 |
|
22
|
|
|
* @since 1.0.0 |
|
23
|
|
|
*/ |
|
24
|
|
|
class Money { |
|
25
|
|
|
/** |
|
26
|
|
|
* Amount value. |
|
27
|
|
|
* |
|
28
|
|
|
* @var float |
|
29
|
|
|
*/ |
|
30
|
|
|
private $value; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Currency. |
|
34
|
|
|
* |
|
35
|
|
|
* @var Currency |
|
36
|
|
|
*/ |
|
37
|
|
|
private $currency; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* Calculator. |
|
41
|
|
|
* |
|
42
|
|
|
* @var Calculator |
|
43
|
|
|
*/ |
|
44
|
|
|
private static $calculator; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Calculators. |
|
48
|
|
|
* |
|
49
|
|
|
* @var array |
|
50
|
|
|
*/ |
|
51
|
|
|
private static $calculators = [ |
|
52
|
|
|
BcMathCalculator::class, |
|
53
|
|
|
PhpCalculator::class, |
|
54
|
|
|
]; |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* Construct and initialize money object. |
|
58
|
|
|
* |
|
59
|
|
|
* @param string|int|float $value Amount value. |
|
60
|
|
|
* @param Currency|string $currency Currency. |
|
61
|
|
|
*/ |
|
62
|
|
|
public function __construct( $value = 0, $currency = 'EUR' ) { |
|
63
|
|
|
$this->set_value( $value ); |
|
64
|
|
|
$this->set_currency( $currency ); |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* Get default format. |
|
69
|
|
|
* |
|
70
|
|
|
* @return string |
|
71
|
|
|
*/ |
|
72
|
|
|
public static function get_default_format() { |
|
73
|
|
|
/* translators: 1: currency symbol, 2: amount value, 3: currency code, note: use non-breaking space! */ |
|
74
|
|
|
$format = _x( '%1$s%2$s %3$s', 'money format', 'pronamic-money' ); |
|
75
|
|
|
// Note: ↳ Non-breaking space. |
|
76
|
|
|
$format = apply_filters( 'pronamic_money_default_format', $format ); |
|
77
|
|
|
|
|
78
|
|
|
return $format; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* Format i18n. |
|
83
|
|
|
* |
|
84
|
|
|
* @param string|null $format Format. |
|
85
|
|
|
* |
|
86
|
|
|
* @return string |
|
87
|
|
|
*/ |
|
88
|
|
|
public function format_i18n( $format = null ) { |
|
89
|
|
|
if ( is_null( $format ) ) { |
|
90
|
|
|
$format = self::get_default_format(); |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
$alphabetic_code = $this->currency->get_alphabetic_code(); |
|
94
|
|
|
|
|
95
|
|
|
if ( ! empty( $alphabetic_code ) ) { |
|
96
|
|
|
$number_decimals = $this->currency->get_number_decimals(); |
|
97
|
|
|
|
|
98
|
|
|
// Handle non trailing zero formatter. |
|
99
|
|
|
if ( false !== strpos( $format, '%2$NTZ' ) ) { |
|
100
|
|
|
$decimals = substr( $this->format(), ( - 1 * $number_decimals ), $number_decimals ); |
|
101
|
|
|
|
|
102
|
|
|
if ( 0 === (int) $decimals ) { |
|
103
|
|
|
$number_decimals = 0; |
|
104
|
|
|
} |
|
105
|
|
|
|
|
106
|
|
|
$format = str_replace( '%2$NTZ', '%2$s', $format ); |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
|
|
return sprintf( |
|
110
|
|
|
$format, |
|
111
|
|
|
$this->currency->get_symbol(), |
|
112
|
|
|
number_format_i18n( $this->get_value(), $number_decimals ), |
|
113
|
|
|
$this->currency->get_alphabetic_code() |
|
114
|
|
|
); |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
return number_format_i18n( $this->get_value(), 2 ); |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* Format i18n without trailing zeros. |
|
122
|
|
|
* |
|
123
|
|
|
* @param string|null $format Format. |
|
124
|
|
|
* |
|
125
|
|
|
* @return string |
|
126
|
|
|
*/ |
|
127
|
|
|
public function format_i18n_non_trailing_zeros( $format = null ) { |
|
128
|
|
|
if ( is_null( $format ) ) { |
|
129
|
|
|
$format = self::get_default_format(); |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
$format = str_replace( '%2$s', '%2$NTZ', $format ); |
|
133
|
|
|
|
|
134
|
|
|
return $this->format_i18n( $format ); |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
/** |
|
138
|
|
|
* Format. |
|
139
|
|
|
* |
|
140
|
|
|
* @param string|null $format Format. |
|
141
|
|
|
* |
|
142
|
|
|
* @return string |
|
143
|
|
|
*/ |
|
144
|
|
|
public function format( $format = null ) { |
|
145
|
|
|
if ( is_null( $format ) ) { |
|
146
|
|
|
$format = '%2$s'; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
$alphabetic_code = $this->currency->get_alphabetic_code(); |
|
150
|
|
|
|
|
151
|
|
|
if ( ! empty( $alphabetic_code ) ) { |
|
152
|
|
|
return sprintf( |
|
153
|
|
|
$format, |
|
154
|
|
|
$this->currency->get_symbol(), |
|
155
|
|
|
number_format( $this->get_value(), $this->get_currency()->get_number_decimals(), '.', '' ), |
|
156
|
|
|
$this->currency->get_alphabetic_code() |
|
157
|
|
|
); |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
return number_format( $this->get_value(), 2, '.', '' ); |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
/** |
|
164
|
|
|
* Get value. |
|
165
|
|
|
* |
|
166
|
|
|
* @return float Amount value. |
|
167
|
|
|
*/ |
|
168
|
|
|
public function get_value() { |
|
169
|
|
|
return $this->value; |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
/** |
|
173
|
|
|
* Get amount. |
|
174
|
|
|
* |
|
175
|
|
|
* @deprecated 1.2.0 |
|
176
|
|
|
* @return float Amount value. |
|
177
|
|
|
*/ |
|
178
|
|
|
public function get_amount() { |
|
179
|
|
|
_deprecated_function( __METHOD__, '1.2.0', 'Money::get_value()' ); |
|
180
|
|
|
|
|
181
|
|
|
return $this->get_value(); |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
/** |
|
185
|
|
|
* Get cents. |
|
186
|
|
|
* |
|
187
|
|
|
* @return float |
|
188
|
|
|
* |
|
189
|
|
|
* @deprecated 1.2.2 Use `Money::get_minor_units()` instead. |
|
190
|
|
|
*/ |
|
191
|
|
|
public function get_cents() { |
|
192
|
|
|
return (float) $this->get_minor_units(); |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* Get amount in minor units. |
|
197
|
|
|
* |
|
198
|
|
|
* Examples for value 10: |
|
199
|
|
|
* JPY 0 decimals: 10 |
|
200
|
|
|
* EUR 2 decimals: 1000 |
|
201
|
|
|
* BHD 3 decimals: 10000 |
|
202
|
|
|
* NLG 4 decimals: 100000 |
|
203
|
|
|
* |
|
204
|
|
|
* @since 1.2.1 |
|
205
|
|
|
* |
|
206
|
|
|
* @return int |
|
207
|
|
|
*/ |
|
208
|
|
|
public function get_minor_units() { |
|
209
|
|
|
$calculator = $this->get_calculator(); |
|
210
|
|
|
|
|
211
|
|
|
$minor_units = $calculator->multiply( strval( $this->get_value() ), pow( 10, $this->currency->get_number_decimals() ) ); |
|
212
|
|
|
|
|
213
|
|
|
return (int) $minor_units; |
|
214
|
|
|
} |
|
215
|
|
|
|
|
216
|
|
|
/** |
|
217
|
|
|
* Set value. |
|
218
|
|
|
* |
|
219
|
|
|
* @param mixed $value Amount value. |
|
220
|
|
|
*/ |
|
221
|
|
|
public function set_value( $value ) { |
|
222
|
|
|
$this->value = floatval( $value ); |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
/** |
|
226
|
|
|
* Set amount. |
|
227
|
|
|
* |
|
228
|
|
|
* @deprecated 1.2.0 |
|
229
|
|
|
* @param mixed $value Amount value. |
|
230
|
|
|
*/ |
|
231
|
|
|
public function set_amount( $value ) { |
|
232
|
|
|
_deprecated_function( __METHOD__, '1.2.0', 'Money::set_value()' ); |
|
233
|
|
|
|
|
234
|
|
|
$this->set_value( $value ); |
|
235
|
|
|
} |
|
236
|
|
|
|
|
237
|
|
|
/** |
|
238
|
|
|
* Get currency. |
|
239
|
|
|
* |
|
240
|
|
|
* @return Currency |
|
241
|
|
|
*/ |
|
242
|
|
|
public function get_currency() { |
|
243
|
|
|
return $this->currency; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* Set currency. |
|
248
|
|
|
* |
|
249
|
|
|
* @param string|Currency $currency Currency. |
|
250
|
|
|
*/ |
|
251
|
|
|
public function set_currency( $currency ) { |
|
252
|
|
|
if ( $currency instanceof Currency ) { |
|
253
|
|
|
$this->currency = $currency; |
|
254
|
|
|
|
|
255
|
|
|
return; |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
$this->currency = Currency::get_instance( $currency ); |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
|
/** |
|
262
|
|
|
* Create a string representation of this money object. |
|
263
|
|
|
* |
|
264
|
|
|
* @return string |
|
265
|
|
|
*/ |
|
266
|
|
|
public function __toString() { |
|
267
|
|
|
return $this->format_i18n(); |
|
268
|
|
|
} |
|
269
|
|
|
|
|
270
|
|
|
/** |
|
271
|
|
|
* Returns a new Money object that represents |
|
272
|
|
|
* the sum of this and an other Money object. |
|
273
|
|
|
* |
|
274
|
|
|
* @param Money $addend Addend. |
|
275
|
|
|
* |
|
276
|
|
|
* @return Money |
|
277
|
|
|
*/ |
|
278
|
|
View Code Duplication |
public function add( Money $addend ) { |
|
|
|
|
|
|
279
|
|
|
$value = $this->get_value(); |
|
280
|
|
|
|
|
281
|
|
|
$calculator = $this->get_calculator(); |
|
282
|
|
|
|
|
283
|
|
|
$value = $calculator->add( strval( $value ), strval( $addend->get_value() ) ); |
|
284
|
|
|
|
|
285
|
|
|
return new self( $value, $this->get_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
|
|
|
* |
|
294
|
|
|
* @param Money $subtrahend Subtrahend. |
|
295
|
|
|
* |
|
296
|
|
|
* @return Money |
|
297
|
|
|
*/ |
|
298
|
|
View Code Duplication |
public function subtract( Money $subtrahend ) { |
|
|
|
|
|
|
299
|
|
|
$value = $this->get_value(); |
|
300
|
|
|
|
|
301
|
|
|
$calculator = $this->get_calculator(); |
|
302
|
|
|
|
|
303
|
|
|
$value = $calculator->subtract( strval( $value ), strval( $subtrahend->get_value() ) ); |
|
304
|
|
|
|
|
305
|
|
|
return new self( $value, $this->get_currency() ); |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
|
|
/** |
|
309
|
|
|
* Initialize calculator. |
|
310
|
|
|
* |
|
311
|
|
|
* @return Calculator |
|
312
|
|
|
* |
|
313
|
|
|
* @throws \RuntimeException If cannot find calculator for money calculations. |
|
314
|
|
|
*/ |
|
315
|
|
|
private static function initialize_calculator() { |
|
316
|
|
|
$calculators = self::$calculators; |
|
317
|
|
|
|
|
318
|
|
|
foreach ( $calculators as $calculator ) { |
|
319
|
|
|
if ( $calculator::supported() ) { |
|
320
|
|
|
return new $calculator(); |
|
321
|
|
|
} |
|
322
|
|
|
} |
|
323
|
|
|
|
|
324
|
|
|
throw new \RuntimeException( 'Cannot find calculator for money calculations' ); |
|
325
|
|
|
} |
|
326
|
|
|
|
|
327
|
|
|
/** |
|
328
|
|
|
* Get calculator. |
|
329
|
|
|
* |
|
330
|
|
|
* @return Calculator |
|
331
|
|
|
*/ |
|
332
|
|
|
protected function get_calculator() { |
|
333
|
|
|
if ( null === self::$calculator ) { |
|
334
|
|
|
self::$calculator = self::initialize_calculator(); |
|
335
|
|
|
} |
|
336
|
|
|
|
|
337
|
|
|
return self::$calculator; |
|
338
|
|
|
} |
|
339
|
|
|
} |
|
340
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.