Completed
Push — master ( d2d28e...1c2760 )
by mw
35:37
created

includes/datavalues/SMW_DV_Quantity.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use SMW\DataValues\UnitConversionFetcher;
4
use SMW\Message;
5
use SMW\ApplicationFactory;
6
7
/**
8
 * @ingroup SMWDataValues
9
 */
10
11
/**
12
 * This datavalue implements unit support custom units, for which users have
13
 * provided linear conversion factors within the wiki. Those user settings
14
 * are retrieved from a property page associated with this object.
15
 *
16
 * @author Markus Krötzsch
17
 * @ingroup SMWDataValues
18
 */
19
class SMWQuantityValue extends SMWNumberValue {
20
21
	/**
22
	 * Array with format (canonical unit ID string) => (conversion factor)
23
	 * @var float[]|bool
24
	 */
25
	protected $m_unitfactors = false;
26
27
	/**
28
	 * Array with format (normalised unit string) => (canonical unit ID string)
29
	 * @var string[]|bool
30
	 */
31
	protected $m_unitids = false;
32
33
	/**
34
	 * Ordered array of (normalized) units that should be displayed in tooltips, etc.
35
	 * @var string[]|bool
36
	 */
37
	protected $m_displayunits = false;
38
39
	/**
40
	 * Main unit in canonical form (recognised by the conversion factor 1)
41
	 * @var string|bool
42
	 */
43
	protected $m_mainunit = false;
44
45 22
	protected function convertToMainUnit( $number, $unit ) {
46 22
		$this->initConversionData();
47
48 22
		if ( array_key_exists( $unit, $this->m_unitids ) ) {
49 21
			$this->m_unitin = $this->m_unitids[$unit];
50 21
			assert( $this->m_unitfactors[$this->m_unitin] != 0 /* Should be filtered by initConversionData() */ );
51 21
			$this->m_dataitem = new SMWDINumber( $number / $this->m_unitfactors[$this->m_unitin], $this->m_typeid );
52 21
			return true;
53
		} else { // unsupported unit
54 7
			return false;
55
		}
56
	}
57
58 13
	protected function makeConversionValues() {
59 13
		if ( $this->m_unitvalues !== false ) {
60
			return; // do this only once
61
		}
62
63 13
		$this->m_unitvalues = array();
64
65 13
		if ( !$this->isValid() ) {
66
			return;
67
		}
68
69 13
		$this->initDisplayData();
70
71 13
		if ( count( $this->m_displayunits ) == 0 ) { // no display units, just show all
72 11
			foreach ( $this->m_unitfactors as $unit => $factor ) {
0 ignored issues
show
The expression $this->m_unitfactors of type array<integer,double>|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
73 11
				if ( $unit !== '' ) { // filter out the empty fallback unit that is always there
74 11
					$this->m_unitvalues[$unit] = $this->m_dataitem->getNumber() * $factor;
75
				}
76
			}
77
		} else {
78 3
			foreach ( $this->m_displayunits as $unit ) {
79
				/// NOTE We keep non-ID units unless the input unit is used, so display units can be used to pick
80
				/// the preferred form of a unit. Doing this requires us to recompute the conversion values whenever
81
				/// the m_unitin changes.
82 3
				$unitkey = ( $this->m_unitids[$unit] == $this->m_unitin ) ? $this->m_unitids[$unit] : $unit;
83 3
				$this->m_unitvalues[$unitkey] = $this->m_dataitem->getNumber() * $this->m_unitfactors[$this->m_unitids[$unit]];
84
			}
85
		}
86 13
	}
87
88 8
	protected function makeUserValue() {
89 8
		$printunit = false; // the normalised string of a known unit to use for printouts
90
91
		// Check if a known unit is given as outputformat:
92 8
		if ( ( $this->m_outformat ) && ( $this->m_outformat != '-' ) &&
93 8
		     ( $this->m_outformat != '-n' ) && ( $this->m_outformat != '-u' ) ) { // first try given output unit
94 2
			$wantedunit = $this->normalizeUnit( $this->m_outformat );
95 2
			if ( array_key_exists( $wantedunit, $this->m_unitids ) ) {
96 2
				$printunit = $wantedunit;
97
			}
98
		}
99
100
		// Alternatively, try to use the main display unit as a default:
101 8
		if ( $printunit === false ) {
102 8
			$this->initDisplayData();
103 8
			if ( count( $this->m_displayunits ) > 0 ) {
104 3
				$printunit = reset( $this->m_displayunits );
105
			}
106
		}
107
		// Finally, fall back to main unit:
108 8
		if ( $printunit === false ) {
109 6
			$printunit = $this->getUnit();
110
		}
111
112 8
		$asPrefix = isset( $this->prefixalUnitPreference[$printunit] ) && $this->prefixalUnitPreference[$printunit];
113
114 8
		$this->m_unitin = $this->m_unitids[$printunit];
115 8
		$this->m_unitvalues = false; // this array depends on m_unitin if displayunits were used, better invalidate it here
116
117 8
		$value = $this->m_dataitem->getNumber() * $this->m_unitfactors[$this->m_unitin];
118
119 8
		$this->m_caption = '';
120
121 8
		if ( $this->m_outformat != '-u' ) { // -u is the format for displaying the unit only
122 8
			$this->m_caption .= ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ? $this->getLocalizedFormattedNumber( $value ) : $this->getNormalizedFormattedNumber( $value ) );
123
		}
124
125 8
		if ( ( $printunit !== '' ) && ( $this->m_outformat != '-n' ) ) { // -n is the format for displaying the number only
126
127 8
			$sep = '';
128
129 8
			if ( $this->m_outformat != '-u' ) {
130 8
				$sep =  ( $this->m_outformat != '-' ? '&#160;' : ' ' );
131
			}
132
133 8
			$this->m_caption = $asPrefix ? $printunit . $sep . $this->m_caption : $this->m_caption . $sep . $printunit;
134
		}
135 8
	}
136
137
	public function getUnitList() {
138
		$this->initConversionData();
139
		return array_keys( $this->m_unitfactors );
140
	}
141
142 7
	public function getUnit() {
143 7
		$this->initConversionData();
144 7
		return $this->m_mainunit;
145
	}
146
147
/// The remaining functions are relatively "private" but are kept protected since
148
/// subclasses might exploit this to, e.g., "fake" conversion factors instead of
149
/// getting them from the database. A cheap way of making built-in types.
150
151
	/**
152
	 * This method initializes $m_unitfactors, $m_unitids, and $m_mainunit.
153
	 */
154 22
	protected function initConversionData() {
155 22
		if ( $this->m_unitids !== false ) {
156 14
			return; // do the below only once
157
		}
158
159 22
		$unitConversionFetcher = new UnitConversionFetcher( $this );
160 22
		$unitConversionFetcher->fetchCachedConversionData( $this->m_property );
161
162 22
		if ( $unitConversionFetcher->getErrors() !== array() ) {
163 7
			foreach ( $unitConversionFetcher->getErrors() as $error ) {
164 7
				$this->addErrorMsg(
165
					$error,
166 7
					Message::TEXT,
167 7
					Message::USER_LANGUAGE
168
				);
169
			}
170
		}
171
172 22
		$this->m_unitids = $unitConversionFetcher->getUnitIds();
173 22
		$this->m_unitfactors = $unitConversionFetcher->getUnitFactors();
174 22
		$this->m_mainunit = $unitConversionFetcher->getMainUnit();
175 22
		$this->prefixalUnitPreference = $unitConversionFetcher->getPrefixalUnitPreference();
176 22
	}
177
178
	/**
179
	 * This method initializes $m_displayunits.
180
	 */
181 14
	protected function initDisplayData() {
182 14
		if ( $this->m_displayunits !== false ) {
183 6
			return; // do the below only once
184
		}
185 14
		$this->initConversionData(); // needed to normalise unit strings
186 14
		$this->m_displayunits = array();
187
188 14
		if ( is_null( $this->m_property ) || is_null( $this->m_property->getDIWikiPage() ) ) {
189
			return;
190
		}
191
192 14
		$units = ApplicationFactory::getInstance()->getPropertySpecificationLookup()->getDisplayUnitsBy(
193 14
			$this->getProperty()
194
		);
195
196 14
		foreach ( $units as $unit ) {
197 4
			$unit = $this->normalizeUnit( $unit );
198 4
			if ( array_key_exists( $unit, $this->m_unitids ) ) {
199 4
				$this->m_displayunits[] = $unit; // do not avoid duplicates, users can handle this
200
			} // note: we ignore unsuppported units -- no way to display them
201
		}
202 14
	}
203
}
204