1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace DataValues; |
4
|
|
|
|
5
|
|
|
use InvalidArgumentException; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Class representing a quantity with associated unit and uncertainty interval. |
9
|
|
|
* The amount is stored as a @see DecimalValue object. |
10
|
|
|
* |
11
|
|
|
* For simple numeric amounts use @see NumberValue. |
12
|
|
|
* |
13
|
|
|
* @since 0.1 |
14
|
|
|
* |
15
|
|
|
* @licence GNU GPL v2+ |
16
|
|
|
* @author Daniel Kinzler |
17
|
|
|
*/ |
18
|
|
|
class QuantityValue extends DataValueObject { |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* The quantity's amount |
22
|
|
|
* |
23
|
|
|
* @var DecimalValue |
24
|
|
|
*/ |
25
|
|
|
private $amount; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* The quantity's unit identifier (use "1" for unitless quantities). |
29
|
|
|
* |
30
|
|
|
* @var string |
31
|
|
|
*/ |
32
|
|
|
private $unit; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* The quantity's upper bound |
36
|
|
|
* |
37
|
|
|
* @var DecimalValue|null |
38
|
|
|
*/ |
39
|
|
|
private $upperBound; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* The quantity's lower bound |
43
|
|
|
* |
44
|
|
|
* @var DecimalValue|null |
45
|
|
|
*/ |
46
|
|
|
private $lowerBound; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Constructs a new QuantityValue object, representing the given value. |
50
|
|
|
* |
51
|
|
|
* @since 0.1 |
52
|
|
|
* |
53
|
|
|
* @param DecimalValue $amount |
54
|
|
|
* @param string $unit A unit identifier. Must not be empty, use "1" for unit-less quantities. |
55
|
|
|
* @param DecimalValue|null $upperBound The upper bound of the quantity, inclusive. |
56
|
|
|
* @param DecimalValue|null $lowerBound The lower bound of the quantity, inclusive. |
57
|
|
|
* |
58
|
|
|
* @throws IllegalValueException |
59
|
|
|
*/ |
60
|
|
|
public function __construct( |
61
|
|
|
DecimalValue $amount, |
62
|
|
|
$unit, |
63
|
|
|
DecimalValue $upperBound = null, |
64
|
|
|
DecimalValue $lowerBound = null |
65
|
|
|
) { |
66
|
|
|
if ( ( $lowerBound && !$upperBound ) || ( !$lowerBound && $upperBound ) ) { |
67
|
|
|
throw new IllegalValueException( 'Either both or no bounds should be defined.' ); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
if ( $lowerBound && $lowerBound->compare( $amount ) > 0 ) { |
71
|
|
|
throw new IllegalValueException( '$lowerBound ' . $lowerBound->getValue() . ' must be <= $amount ' . $amount->getValue() ); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
if ( $upperBound && $upperBound->compare( $amount ) < 0 ) { |
75
|
|
|
throw new IllegalValueException( '$upperBound ' . $upperBound->getValue() . ' must be >= $amount ' . $amount->getValue() ); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
if ( !is_string( $unit ) ) { |
79
|
|
|
throw new IllegalValueException( '$unit needs to be a string, not ' . gettype( $unit ) ); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
if ( $unit === '' ) { |
83
|
|
|
throw new IllegalValueException( '$unit can not be an empty string (use "1" for unit-less quantities)' ); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$this->amount = $amount; |
87
|
|
|
$this->unit = $unit; |
88
|
|
|
$this->upperBound = $upperBound; |
89
|
|
|
$this->lowerBound = $lowerBound; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Returns a QuantityValue representing the given amount. |
94
|
|
|
* If no upper or lower bound is given, the amount is assumed to be absolutely exact, |
95
|
|
|
* that is, the amount itself will be used as the upper and lower bound. |
96
|
|
|
* |
97
|
|
|
* This is a convenience wrapper around the constructor that accepts native values |
98
|
|
|
* instead of DecimalValue objects. |
99
|
|
|
* |
100
|
|
|
* @note: if the amount or a bound is given as a string, the string must conform |
101
|
|
|
* to the rules defined by @see DecimalValue. |
102
|
|
|
* |
103
|
|
|
* @since 0.1 |
104
|
|
|
* |
105
|
|
|
* @param string|int|float|DecimalValue $amount |
106
|
|
|
* @param string $unit A unit identifier. Must not be empty, use "1" for unit-less quantities. |
107
|
|
|
* @param string|int|float|DecimalValue|null $upperBound |
108
|
|
|
* @param string|int|float|DecimalValue|null $lowerBound |
109
|
|
|
* |
110
|
|
|
* @return QuantityValue |
111
|
|
|
* @throws IllegalValueException |
112
|
|
|
*/ |
113
|
|
|
public static function newFromNumber( $amount, $unit = '1', $upperBound = null, $lowerBound = null ) { |
114
|
|
|
$amount = self::asDecimalValue( 'amount', $amount ); |
115
|
|
|
$upperBound = ( $upperBound === null || $upperBound === '' ) |
116
|
|
|
? null |
117
|
|
|
: self::asDecimalValue( 'upperBound', $upperBound ); |
118
|
|
|
|
119
|
|
|
$lowerBound = ( $lowerBound === null || $lowerBound === '' ) |
120
|
|
|
? null |
121
|
|
|
: self::asDecimalValue( 'lowerBound', $lowerBound ); |
122
|
|
|
|
123
|
|
|
return new self( $amount, $unit, $upperBound, $lowerBound ); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @see newFromNumber |
128
|
|
|
* |
129
|
|
|
* @deprecated since 0.1, use newFromNumber instead |
130
|
|
|
* |
131
|
|
|
* @param string|int|float|DecimalValue $amount |
132
|
|
|
* @param string $unit |
133
|
|
|
* @param string|int|float|DecimalValue|null $upperBound |
134
|
|
|
* @param string|int|float|DecimalValue|null $lowerBound |
135
|
|
|
* |
136
|
|
|
* @return QuantityValue |
137
|
|
|
*/ |
138
|
|
|
public static function newFromDecimal( $amount, $unit = '1', $upperBound = null, $lowerBound = null ) { |
139
|
|
|
return self::newFromNumber( $amount, $unit, $upperBound, $lowerBound ); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Converts $number to a DecimalValue if possible and necessary. |
144
|
|
|
* |
145
|
|
|
* @note: if the $number is given as a string, it must conform to the rules |
146
|
|
|
* defined by @see DecimalValue. |
147
|
|
|
* |
148
|
|
|
* @param string $name The variable name to use in exception messages |
149
|
|
|
* @param string|int|float|DecimalValue|null $number |
150
|
|
|
* |
151
|
|
|
* @throws IllegalValueException |
152
|
|
|
* @throws InvalidArgumentException |
153
|
|
|
* @return DecimalValue |
154
|
|
|
*/ |
155
|
|
|
private static function asDecimalValue( $name, $number ) { |
156
|
|
|
if ( !is_string( $name ) ) { |
157
|
|
|
throw new InvalidArgumentException( '$name must be a string' ); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
if ( $number instanceof DecimalValue ) { |
|
|
|
|
161
|
|
|
// nothing to do |
162
|
|
|
} elseif ( is_int( $number ) || is_float( $number ) || is_string( $number ) ) { |
163
|
|
|
$number = new DecimalValue( $number ); |
164
|
|
|
} else { |
165
|
|
|
throw new IllegalValueException( '$' . $name . ' must be a string, int, or float' ); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
return $number; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @see Serializable::serialize |
173
|
|
|
* |
174
|
|
|
* @since 0.1 |
175
|
|
|
* |
176
|
|
|
* @return string |
177
|
|
|
*/ |
178
|
|
|
public function serialize() { |
179
|
|
|
$fields = array( |
180
|
|
|
$this->amount, |
181
|
|
|
$this->unit |
182
|
|
|
); |
183
|
|
|
|
184
|
|
|
if ( $this->upperBound ) { |
185
|
|
|
$fields[] = $this->upperBound; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
if ( $this->lowerBound ) { |
189
|
|
|
$fields[] = $this->lowerBound; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return serialize( $fields ); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @see Serializable::unserialize |
197
|
|
|
* |
198
|
|
|
* @since 0.1 |
199
|
|
|
* |
200
|
|
|
* @param string $data |
201
|
|
|
*/ |
202
|
|
|
public function unserialize( $data ) { |
203
|
|
|
$fields = unserialize( $data ); |
204
|
|
|
$amount = $fields[0]; |
205
|
|
|
$unit = $fields[1]; |
206
|
|
|
|
207
|
|
|
$upperBound = isset( $fields[2] ) ? $fields[2] : null; |
208
|
|
|
$lowerBound = isset( $fields[3] ) ? $fields[3] : null; |
209
|
|
|
|
210
|
|
|
$this->__construct( $amount, $unit, $upperBound, $lowerBound ); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* @see DataValue::getType |
215
|
|
|
* |
216
|
|
|
* @since 0.1 |
217
|
|
|
* |
218
|
|
|
* @return string |
219
|
|
|
*/ |
220
|
|
|
public static function getType() { |
221
|
|
|
return 'quantity'; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Whether this Quantity has a known upper and lower bound. |
226
|
|
|
* If this returns true, getUpperBound() and getLowerBound() will return a DecimalValue. |
227
|
|
|
* If this returns false, getUpperBound() and getLowerBound() will return null. |
228
|
|
|
* |
229
|
|
|
* @return bool |
230
|
|
|
*/ |
231
|
|
|
public function hasKnownBounds() { |
232
|
|
|
return $this->upperBound !== null && $this->lowerBound !== null; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @see DataValue::getSortKey |
237
|
|
|
* |
238
|
|
|
* @since 0.1 |
239
|
|
|
* |
240
|
|
|
* @return float |
241
|
|
|
*/ |
242
|
|
|
public function getSortKey() { |
243
|
|
|
return $this->getAmount()->getValueFloat(); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Returns the quantity object. |
248
|
|
|
* @see DataValue::getValue |
249
|
|
|
* |
250
|
|
|
* @since 0.1 |
251
|
|
|
* |
252
|
|
|
* @return QuantityValue |
253
|
|
|
*/ |
254
|
|
|
public function getValue() { |
255
|
|
|
return $this; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Returns the amount represented by this quantity. |
260
|
|
|
* |
261
|
|
|
* @since 0.1 |
262
|
|
|
* |
263
|
|
|
* @return DecimalValue |
264
|
|
|
*/ |
265
|
|
|
public function getAmount() { |
266
|
|
|
return $this->amount; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Returns this quantity's upper bound, or null if the bounds are not known. |
271
|
|
|
* |
272
|
|
|
* @since 0.1 |
273
|
|
|
* |
274
|
|
|
* @return DecimalValue|null |
275
|
|
|
*/ |
276
|
|
|
public function getUpperBound() { |
277
|
|
|
return $this->upperBound; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Returns this quantity's lower bound, or null if the bounds are not known. |
282
|
|
|
* |
283
|
|
|
* @since 0.1 |
284
|
|
|
* |
285
|
|
|
* @return DecimalValue|null |
286
|
|
|
*/ |
287
|
|
|
public function getLowerBound() { |
288
|
|
|
return $this->lowerBound; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Returns the size of the uncertainty interval. |
293
|
|
|
* This can roughly be interpreted as "amount +/- uncertainty/2". |
294
|
|
|
* |
295
|
|
|
* The exact interpretation of the uncertainty interval is left to the concrete application or |
296
|
|
|
* data point. For example, the uncertainty interval may be defined to be that part of a |
297
|
|
|
* normal distribution that is required to cover the 95th percentile. |
298
|
|
|
* |
299
|
|
|
* @since 0.1 |
300
|
|
|
* |
301
|
|
|
* @return float|bool The uncertainty magnitude, or false if the bounds for this Quantity |
302
|
|
|
* are not known. |
303
|
|
|
*/ |
304
|
|
|
public function getUncertainty() { |
305
|
|
|
if ( !$this->hasKnownBounds() ) { |
306
|
|
|
return false; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
return $this->getUpperBound()->getValueFloat() - $this->getLowerBound()->getValueFloat(); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Returns a DecimalValue representing the symmetrical offset to be applied |
314
|
|
|
* to the raw amount for a rough representation of the uncertainty interval, |
315
|
|
|
* as in "amount +/- offset". |
316
|
|
|
* |
317
|
|
|
* The offset is calculated as max( amount - lowerBound, upperBound - amount ). |
318
|
|
|
* |
319
|
|
|
* @since 0.1 |
320
|
|
|
* |
321
|
|
|
* @return DecimalValue|null The size of the uncertainty margin, or null if the bounds |
322
|
|
|
* for this Quantity are not known. |
323
|
|
|
*/ |
324
|
|
|
public function getUncertaintyMargin() { |
325
|
|
|
if ( !$this->hasKnownBounds() ) { |
326
|
|
|
return null; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
$math = new DecimalMath(); |
330
|
|
|
|
331
|
|
|
$lowerMargin = $math->sum( $this->getAmount(), $this->getLowerBound()->computeComplement() ); |
332
|
|
|
$upperMargin = $math->sum( $this->getUpperBound(), $this->getAmount()->computeComplement() ); |
|
|
|
|
333
|
|
|
|
334
|
|
|
$margin = $math->max( $lowerMargin, $upperMargin ); |
335
|
|
|
return $margin; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Returns the order of magnitude of the uncertainty as the exponent of |
340
|
|
|
* last significant digit in the amount-string. The value returned by this |
341
|
|
|
* is suitable for use with @see DecimalMath::roundToExponent(). |
342
|
|
|
* |
343
|
|
|
* @example: if two digits after the decimal point are significant, this |
344
|
|
|
* returns -2. |
345
|
|
|
* |
346
|
|
|
* @example: if the last two digits before the decimal point are insignificant, |
347
|
|
|
* this returns 2. |
348
|
|
|
* |
349
|
|
|
* Note that this calculation assumes a symmetric uncertainty interval, |
350
|
|
|
* and can be misleading. |
351
|
|
|
* |
352
|
|
|
* A quantity without known bounds is treated as absolutely precise in this context. |
353
|
|
|
* |
354
|
|
|
* @since 0.1 |
355
|
|
|
* |
356
|
|
|
* @return int The order of uncertainty |
357
|
|
|
*/ |
358
|
|
|
public function getOrderOfUncertainty() { |
359
|
|
|
if ( !$this->hasKnownBounds() ) { |
360
|
|
|
$precision = 0.0; |
361
|
|
|
} else { |
362
|
|
|
// the desired precision is given by the distance between the amount and |
363
|
|
|
// whatever is closer, the upper or lower bound. |
364
|
|
|
//TODO: use DecimalMath to avoid floating point errors! |
365
|
|
|
$amount = $this->getAmount()->getValueFloat(); |
366
|
|
|
$upperBound = $this->getUpperBound()->getValueFloat(); |
367
|
|
|
$lowerBound = $this->getLowerBound()->getValueFloat(); |
368
|
|
|
$precision = min( $amount - $lowerBound, $upperBound - $amount ); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
if ( $precision === 0.0 ) { |
372
|
|
|
// If there is no uncertainty, the order of uncertainty is a bit more than |
373
|
|
|
// what we have digits for. |
374
|
|
|
return -strlen( $this->amount->getFractionalPart() ); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
// e.g. +/- 200 -> 2; +/- 0.02 -> -2 |
378
|
|
|
// note: we really want floor( log10( $precision ) ), but have to account for |
379
|
|
|
// small errors made in the floating point operations above. |
380
|
|
|
// @todo: use bcmath (via DecimalMath) to avoid this if possible |
381
|
|
|
$orderOfUncertainty = floor( log10( $precision + 0.0000000005 ) ); |
382
|
|
|
|
383
|
|
|
return (int)$orderOfUncertainty; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Returns the number of significant figures in the amount-string, |
388
|
|
|
* counting the decimal point, but not counting the leading sign. |
389
|
|
|
* |
390
|
|
|
* Note that this calculation assumes a symmetric uncertainty interval, and can be misleading |
391
|
|
|
* |
392
|
|
|
* @since 0.1 |
393
|
|
|
* |
394
|
|
|
* @return int |
395
|
|
|
*/ |
396
|
|
|
public function getSignificantFigures() { |
397
|
|
|
$math = new DecimalMath(); |
398
|
|
|
|
399
|
|
|
// $orderOfUncertainty is +/- 200 -> 2; +/- 0.02 -> -2 |
400
|
|
|
$orderOfUncertainty = $this->getOrderOfUncertainty(); |
401
|
|
|
|
402
|
|
|
// the number of digits (without the sign) is the same as the position (with the sign). |
403
|
|
|
$significantDigits = $math->getPositionForExponent( $orderOfUncertainty, $this->amount ); |
404
|
|
|
|
405
|
|
|
return $significantDigits; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* Returns the unit held by this quantity. |
410
|
|
|
* Unit-less quantities should use "1" as their unit. |
411
|
|
|
* |
412
|
|
|
* @since 0.1 |
413
|
|
|
* |
414
|
|
|
* @return string |
415
|
|
|
*/ |
416
|
|
|
public function getUnit() { |
417
|
|
|
return $this->unit; |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* Returns a transformed value derived from this QuantityValue by applying |
422
|
|
|
* the given transformation to the amount and the upper and lower bounds. |
423
|
|
|
* The resulting amount and bounds are rounded to the significant number of |
424
|
|
|
* digits. Note that for exact quantities (with at least one bound equal to |
425
|
|
|
* the amount), no rounding is applied (since they are considered to have |
426
|
|
|
* infinite precision). |
427
|
|
|
* |
428
|
|
|
* The transformation is provided as a callback, which must implement a |
429
|
|
|
* monotonously increasing, fully differentiable function mapping a DecimalValue |
430
|
|
|
* to a DecimalValue. Typically, it will be a linear transformation applying a |
431
|
|
|
* factor and an offset. |
432
|
|
|
* |
433
|
|
|
* @param string $newUnit The unit of the transformed quantity. |
434
|
|
|
* |
435
|
|
|
* @param callable $transformation A callback that implements the desired transformation. |
436
|
|
|
* The transformation will be called three times, once for the amount, once |
437
|
|
|
* for the lower bound, and once for the upper bound. It must return a DecimalValue. |
438
|
|
|
* The first parameter passed to $transformation is the DecimalValue to transform |
439
|
|
|
* In addition, any extra parameters passed to transform() will be passed through |
440
|
|
|
* to the transformation callback. |
441
|
|
|
* |
442
|
|
|
* @param mixed ... Any extra parameters will be passed to the $transformation function. |
443
|
|
|
* |
444
|
|
|
* @throws InvalidArgumentException |
445
|
|
|
* @return QuantityValue |
446
|
|
|
*/ |
447
|
|
|
public function transform( $newUnit, $transformation ) { |
448
|
|
|
if ( !is_callable( $transformation ) ) { |
449
|
|
|
throw new InvalidArgumentException( '$transformation must be callable.' ); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
if ( !is_string( $newUnit ) ) { |
453
|
|
|
throw new InvalidArgumentException( '$newUnit must be a string. Use "1" as the unit for unit-less quantities.' ); |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
if ( $newUnit === '' ) { |
457
|
|
|
throw new InvalidArgumentException( '$newUnit must not be empty. Use "1" as the unit for unit-less quantities.' ); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
$oldUnit = $this->getUnit(); |
461
|
|
|
|
462
|
|
|
if ( $newUnit === null ) { |
463
|
|
|
$newUnit = $oldUnit; |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
// Apply transformation by calling the $transform callback. |
467
|
|
|
// The first argument for the callback is the DataValue to transform. In addition, |
468
|
|
|
// any extra arguments given for transform() are passed through. |
469
|
|
|
$args = func_get_args(); |
470
|
|
|
array_shift( $args ); |
471
|
|
|
|
472
|
|
|
$args[0] = $this->getAmount(); |
473
|
|
|
$amount = call_user_func_array( $transformation, $args ); |
474
|
|
|
|
475
|
|
|
$args[0] = $this->getUpperBound(); |
476
|
|
|
$upperBound = $args[0] === null ? null : call_user_func_array( $transformation, $args ); |
477
|
|
|
|
478
|
|
|
$args[0] = $this->getLowerBound(); |
479
|
|
|
$lowerBound = $args[0] === null ? null : call_user_func_array( $transformation, $args ); |
480
|
|
|
|
481
|
|
|
// use a preliminary QuantityValue to determine the significant number of digits |
482
|
|
|
$transformed = new self( $amount, $newUnit, $upperBound, $lowerBound ); |
483
|
|
|
$roundingExponent = $transformed->getOrderOfUncertainty(); |
484
|
|
|
|
485
|
|
|
// apply rounding to the significant digits |
486
|
|
|
$math = new DecimalMath(); |
487
|
|
|
|
488
|
|
|
$amount = $math->roundToExponent( $amount, $roundingExponent ); |
489
|
|
|
|
490
|
|
|
if ( $upperBound && $lowerBound ) { |
491
|
|
|
// the constructor makes sure that either both bounds are set, or none. |
492
|
|
|
$upperBound = $math->roundToExponent( $upperBound, $roundingExponent ); |
493
|
|
|
$lowerBound = $math->roundToExponent( $lowerBound, $roundingExponent ); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
return new self( $amount, $newUnit, $upperBound, $lowerBound ); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
public function __toString() { |
500
|
|
|
$unit = $this->getUnit(); |
|
|
|
|
501
|
|
|
|
502
|
|
|
$s = $this->amount->getValue(); |
503
|
|
|
|
504
|
|
|
if ( $this->hasKnownBounds() ) { |
505
|
|
|
$s .= '[' . $this->lowerBound->getValue() . '..' . $this->upperBound->getValue() . ']'; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
if ( $this->unit !== '1' ) { |
509
|
|
|
$s .= $this->unit; |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
return $s; |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
/** |
516
|
|
|
* @see DataValue::getArrayValue |
517
|
|
|
* |
518
|
|
|
* @since 0.1 |
519
|
|
|
* |
520
|
|
|
* @return array |
521
|
|
|
*/ |
522
|
|
|
public function getArrayValue() { |
523
|
|
|
$fields = array( |
524
|
|
|
'amount' => $this->amount->getArrayValue(), |
525
|
|
|
'unit' => $this->unit, |
526
|
|
|
); |
527
|
|
|
|
528
|
|
|
|
529
|
|
|
if ( $this->upperBound ) { |
530
|
|
|
$fields['upperBound'] = $this->upperBound->getArrayValue(); |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
if ( $this->lowerBound ) { |
534
|
|
|
$fields['lowerBound'] = $this->lowerBound->getArrayValue(); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
return $fields; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
/** |
541
|
|
|
* Constructs a new instance of the DataValue from the provided data. |
542
|
|
|
* This can round-trip with @see getArrayValue |
543
|
|
|
* |
544
|
|
|
* @since 0.1 |
545
|
|
|
* |
546
|
|
|
* @param mixed $data |
547
|
|
|
* |
548
|
|
|
* @return QuantityValue |
549
|
|
|
* @throws IllegalValueException |
550
|
|
|
*/ |
551
|
|
|
public static function newFromArray( $data ) { |
552
|
|
|
self::requireArrayFields( $data, array( 'amount', 'unit' ) ); |
553
|
|
|
|
554
|
|
|
$upper = isset( $data['upperBound'] ) |
555
|
|
|
? DecimalValue::newFromArray( $data['upperBound'] ) |
556
|
|
|
: null; |
557
|
|
|
|
558
|
|
|
$lower = isset( $data['lowerBound'] ) |
559
|
|
|
? DecimalValue::newFromArray( $data['lowerBound'] ) |
560
|
|
|
: null; |
561
|
|
|
|
562
|
|
|
return new static( |
563
|
|
|
DecimalValue::newFromArray( $data['amount'] ), |
564
|
|
|
$data['unit'], |
565
|
|
|
$upper, |
566
|
|
|
$lower |
567
|
|
|
); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
/** |
571
|
|
|
* @see Comparable::equals |
572
|
|
|
* |
573
|
|
|
* @since 0.1 |
574
|
|
|
* |
575
|
|
|
* @param mixed $target |
576
|
|
|
* |
577
|
|
|
* @return bool |
578
|
|
|
*/ |
579
|
|
|
public function equals( $target ) { |
580
|
|
|
if ( $this === $target ) { |
581
|
|
|
return true; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
return $target instanceof self |
585
|
|
|
&& $this->toArray() === $target->toArray(); |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
} |
589
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.