Completed
Push — master ( f2f357...ccb861 )
by
unknown
02:21
created

RangeCheckerHelper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Helper;
4
5
use Config;
6
use DataValues\DataValue;
7
use DataValues\QuantityValue;
8
use DataValues\TimeValue;
9
use DataValues\TimeValueCalculator;
10
use DataValues\UnboundedQuantityValue;
11
use InvalidArgumentException;
12
use ValueParsers\ValueParser;
13
use Wikibase\Lib\Units\UnitConverter;
14
use Wikibase\Repo\Parsers\TimeParserFactory;
15
16
/**
17
 * Class for helper functions for range checkers.
18
 *
19
 * @author BP2014N1
20
 * @license GPL-2.0-or-later
21
 */
22
class RangeCheckerHelper {
23
24
	/**
25
	 * @var Config
26
	 */
27
	private $config;
28
29
	/**
30
	 * @var ValueParser
31
	 */
32
	private $timeParser;
33
34
	/**
35
	 * @var TimeValueCalculator
36
	 */
37
	private $timeCalculator;
38
39
	/**
40
	 * @var TimeValueComparer
41
	 */
42
	private $timeValueComparer;
43
44
	/**
45
	 * @var UnitConverter|null
46
	 */
47
	private $unitConverter;
48
49
	public function __construct(
50
		Config $config,
51
		UnitConverter $unitConverter = null
52
	) {
53
		$this->config = $config;
54
		$this->timeParser = ( new TimeParserFactory() )->getTimeParser();
55
		$this->timeCalculator = new TimeValueCalculator();
56
		$this->timeValueComparer = new TimeValueComparer();
57
		$this->unitConverter = $unitConverter;
58
	}
59
60
	/**
61
	 * @param UnboundedQuantityValue $value
62
	 * @return UnboundedQuantityValue $value converted to standard units if possible, otherwise unchanged $value.
63
	 */
64 View Code Duplication
	private function standardize( UnboundedQuantityValue $value ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
65
		if ( $this->unitConverter !== null ) {
66
			$standard = $this->unitConverter->toStandardUnits( $value );
67
			if ( $standard !== null ) {
68
				return $standard;
69
			} else {
70
				return $value;
71
			}
72
		} else {
73
			return $value;
74
		}
75
	}
76
77
	/**
78
	 * Compare two values.
79
	 * If one of them is null, return 0 (equal).
80
	 *
81
	 * @param DataValue|null $lhs left-hand side
82
	 * @param DataValue|null $rhs right-hand side
83
	 *
84
	 * @throws InvalidArgumentException if the values do not both have the same, supported data value type
85
	 * @return integer An integer less than, equal to, or greater than zero
86
	 *                 when $lhs is respectively less than, equal to, or greater than $rhs.
87
	 *                 (In other words, just like the “spaceship” operator <=>.)
88
	 */
89
	public function getComparison( DataValue $lhs = null, DataValue $rhs = null ) {
90
		if ( $lhs === null || $rhs === null ) {
91
			return 0;
92
		}
93
94
		if ( $lhs->getType() !== $rhs->getType() ) {
95
			throw new InvalidArgumentException( 'Different data value types' );
96
		}
97
98
		switch ( $lhs->getType() ) {
99
			case 'time':
100
				/** @var TimeValue $lhs */
101
				/** @var TimeValue $rhs */
102
				return $this->timeValueComparer->getComparison( $lhs, $rhs );
0 ignored issues
show
Documentation introduced by
$lhs is of type object<DataValues\DataValue>, but the function expects a object<DataValues\TimeValue>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
103
			case 'quantity':
104
				/** @var QuantityValue|UnboundedQuantityValue $lhs */
105
				/** @var QuantityValue|UnboundedQuantityValue $rhs */
106
				$lhsStandard = $this->standardize( $lhs );
0 ignored issues
show
Documentation introduced by
$lhs is of type object<DataValues\DataValue>, but the function expects a object<DataValues\UnboundedQuantityValue>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
				$rhsStandard = $this->standardize( $rhs );
108
				return $lhsStandard->getAmount()->compare( $rhsStandard->getAmount() );
109
		}
110
111
		throw new InvalidArgumentException( 'Unsupported data value type' );
112
	}
113
114
	/**
115
	 * Computes $minuend - $subtrahend, in a format depending on the data type.
116
	 * For time values, the difference is in seconds;
117
	 * for quantity values, the difference is the numerical difference between the quantities,
118
	 * after attempting normalization of each side.
119
	 *
120
	 * @param TimeValue|QuantityValue|UnboundedQuantityValue $minuend
121
	 * @param TimeValue|QuantityValue|UnboundedQuantityValue $subtrahend
122
	 *
123
	 * @throws InvalidArgumentException if the values do not both have the same, supported data value type
124
	 * @return UnboundedQuantityValue
125
	 */
126
	public function getDifference( DataValue $minuend, DataValue $subtrahend ) {
127
		if ( $minuend->getType() === 'time' && $subtrahend->getType() === 'time' ) {
128
			$minuendSeconds = $this->timeCalculator->getTimestamp( $minuend );
129
			$subtrahendSeconds = $this->timeCalculator->getTimestamp( $subtrahend );
130
			return UnboundedQuantityValue::newFromNumber(
131
				$minuendSeconds - $subtrahendSeconds,
132
				$this->config->get( 'WBQualityConstraintsSecondUnit' )
133
			);
134
		}
135
		if ( $minuend->getType() === 'quantity' && $subtrahend->getType() === 'quantity' ) {
136
			$minuendStandard = $this->standardize( $minuend );
0 ignored issues
show
Documentation introduced by
$minuend is of type object<DataValues\DataValue>, but the function expects a object<DataValues\UnboundedQuantityValue>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
137
			$subtrahendStandard = $this->standardize( $subtrahend );
0 ignored issues
show
Documentation introduced by
$subtrahend is of type object<DataValues\DataValue>, but the function expects a object<DataValues\UnboundedQuantityValue>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
138
			$minuendValue = $minuendStandard->getAmount()->getValueFloat();
139
			$subtrahendValue = $subtrahendStandard->getAmount()->getValueFloat();
140
			$diff = $minuendValue - $subtrahendValue;
141
			// we don’t check whether both quantities have the same standard unit –
142
			// that’s the job of a different constraint type, Units (T164372)
143
			return UnboundedQuantityValue::newFromNumber( $diff, $minuendStandard->getUnit() );
144
		}
145
146
		throw new InvalidArgumentException( 'Unsupported or different data value types' );
147
	}
148
149
	public function getDifferenceInYears( TimeValue $minuend, TimeValue $subtrahend ) {
150
		if ( !preg_match( '/^([-+]\d{1,16})-(.*)$/', $minuend->getTime(), $minuendMatches ) ||
151
			!preg_match( '/^([-+]\d{1,16})-(.*)$/', $subtrahend->getTime(), $subtrahendMatches )
152
		) {
153
			throw new InvalidArgumentException( 'TimeValue::getTime() did not match expected format' );
154
		}
155
		$minuendYear = (float)$minuendMatches[1];
156
		$subtrahendYear = (float)$subtrahendMatches[1];
157
		$minuendRest = $minuendMatches[2];
158
		$subtrahendRest = $subtrahendMatches[2];
159
160
		// calculate difference of years
161
		$diff = $minuendYear - $subtrahendYear;
162
		if ( $minuendYear > 0.0 && $subtrahendYear < 0.0 ) {
163
			$diff -= 1.0; // there is no year 0, remove it from difference
164
		} elseif ( $minuendYear < 0.0 && $subtrahendYear > 0.0 ) {
165
			$diff -= -1.0; // there is no year 0, remove it from negative difference
166
		}
167
168
		// adjust for date within year by parsing the month-day part within the same year
169
		$minuendDateValue = $this->timeParser->parse( '+0000000000001970-' . $minuendRest );
170
		$subtrahendDateValue = $this->timeParser->parse( '+0000000000001970-' . $subtrahendRest );
171
		$minuendDateSeconds = $this->timeCalculator->getTimestamp( $minuendDateValue );
172
		$subtrahendDateSeconds = $this->timeCalculator->getTimestamp( $subtrahendDateValue );
173
		if ( $minuendDateSeconds < $subtrahendDateSeconds ) {
174
			// difference in the last year is actually less than one full year
175
			// e. g. 1975-03-01 - 1974-09-01 is just six months
176
			// (we don’t need sub-year precision in the difference, adjusting by 0.5 is enough)
177
			$diff -= 0.5;
178
		} elseif ( $minuendDateSeconds > $subtrahendDateSeconds ) {
179
			// difference in the last year is actually more than one full year
180
			// e. g. 1975-09-01 - 1974-03-01 is 18 months
181
			// (we don’t need sub-year precision in the difference, adjusting by 0.5 is enough)
182
			$diff += 0.5;
183
		}
184
185
		$unit = $this->config->get( 'WBQualityConstraintsYearUnit' );
186
		return UnboundedQuantityValue::newFromNumber( $diff, $unit );
187
	}
188
189
	public function isFutureTime( TimeValue $timeValue ) {
190
		return $this->timeValueComparer->isFutureTime( $timeValue );
191
	}
192
193
}
194