Completed
Push — master ( 76f9d5...d59f0b )
by Jeroen De
9s
created

includes/Maps_DistanceParser.php (3 issues)

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
/**
4
 * Static class for distance validation and parsing. Internal representatations are in meters.
5
 *
6
 * TODO: migrate to DataValue, ValueParser and ValueFormatter
7
 *
8
 * @since 0.6
9
 *
10
 * @licence GNU GPL v2+
11
 * @author Jeroen De Dauw < [email protected] >
12
 */
13
class MapsDistanceParser {
14
	
15
	private static $validatedDistanceUnit = false;
16
	
17
	private static $unitRegex = false;
18
	
19
	/**
20
	 * Parses a distance optionally containing a unit to a float value in meters.
21
	 * 
22
	 * @since 0.6
23
	 * 
24
	 * @param string $distance
25
	 * 
26
	 * @return float The distance in meters.
27
	 */
28
	public static function parseDistance( $distance ) {
29
		if ( !self::isDistance( $distance ) ) {
30
			return false;
31
		}
32
		
33
		$distance = self::normalizeDistance( $distance );
34
		
35
		self::initUnitRegex();
36
		
37
		$matches = [];
38
		preg_match( '/^\d+(\.\d+)?\s?(' . self::$unitRegex . ')?$/', $distance, $matches );
39
40
		$value = (float)( $matches[0] . $matches[1] );
41
		$value *= self::getUnitRatio( $matches[2] );
42
		
43
		return $value;
44
	}
45
	
46
	/**
47
	 * Formats a given distance in meters to a distance in an optionally specified notation.
48
	 * 
49
	 * @since 0.6
50
	 * 
51
	 * @param float $meters
52
	 * @param string $unit
53
	 * @param integer $decimals
54
	 * 
55
	 * @return string
56
	 */
57
	public static function formatDistance( $meters, $unit = null, $decimals = 2 ) {
58
		global $wgContLang;
59
		$meters = $wgContLang->formatNum( round( $meters / self::getUnitRatio( $unit ), $decimals ) );
60
		return "$meters $unit";
61
	}
62
	
63
	/**
64
	 * Shortcut for converting from one unit to another.
65
	 * 
66
	 * @since 0.6
67
	 * 
68
	 * @param string $distance
69
	 * @param string $unit
70
	 * @param integer $decimals
71
	 * 
72
	 * @return string
73
	 */
74
	public static function parseAndFormat( $distance, $unit = null, $decimals = 2 ) {
75
		return self::formatDistance( self::parseDistance( $distance ), $unit, $decimals );
0 ignored issues
show
It seems like self::parseDistance($distance) targeting MapsDistanceParser::parseDistance() can also be of type false; however, MapsDistanceParser::formatDistance() does only seem to accept double, did you maybe forget to handle an error condition?
Loading history...
76
	}
77
	
78
	/**
79
	 * Returns if the provided string is a valid distance.
80
	 * 
81
	 * @since 0.6
82
	 * 
83
	 * @param string $distance
84
	 * 
85
	 * @return boolean
86
	 */
87
	public static function isDistance( $distance ) {
88
		$distance = self::normalizeDistance( $distance );
89
		
90
		self::initUnitRegex();
91
92
		return (bool)preg_match( '/^\d+(\.\d+)?\s?(' . self::$unitRegex . ')?$/', $distance );
93
	}
94
	
95
	/**
96
	 * Returns the unit to meter ratio in a safe way, by first resolving the unit.
97
	 * 
98
	 * @since 0.6.2
99
	 * 
100
	 * @param string $unit
101
	 * 
102
	 * @return float
103
	 */
104
	public static function getUnitRatio( $unit = null ) {
105
		global $egMapsDistanceUnits;
106
		return $egMapsDistanceUnits[self::getValidUnit( $unit )];
107
	}
108
	
109
	/**
110
	 * Returns a valid unit. If the provided one is invalid, the default will be used.
111
	 * 
112
	 * @since 0.6.2
113
	 * 
114
	 * @param string $unit
115
	 * 
116
	 * @return string
117
	 */
118
	public static function getValidUnit( $unit = null ) {
119
		global $egMapsDistanceUnit, $egMapsDistanceUnits;
120
		
121
		// This ensures the value for $egMapsDistanceUnit is correct, and caches the result.
122
		if ( self::$validatedDistanceUnit === false ) {
123
			if ( !array_key_exists( $egMapsDistanceUnit, $egMapsDistanceUnits ) ) {
124
				$units = array_keys( $egMapsDistanceUnits );
125
				$egMapsDistanceUnit = $units[0];
126
			}
127
			
128
			self::$validatedDistanceUnit = true;
129
		}		
130
		
131
		if ( $unit == null || !array_key_exists( $unit, $egMapsDistanceUnits ) ) {
0 ignored issues
show
It seems like you are loosely comparing $unit of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
132
			$unit = $egMapsDistanceUnit;
133
		}
134
135
		return $unit;
136
	}
137
	
138
	/**
139
	 * Returns a list of all suported units.
140
	 * 
141
	 * @since 0.6
142
	 * 
143
	 * @return array
144
	 */
145
	public static function getUnits() {
146
		global $egMapsDistanceUnits;
147
		return array_keys( $egMapsDistanceUnits );
148
	}
149
	
150
	/**
151
	 * Normalizes a potential distance by removing spaces and truning comma's into dots.
152
	 * 
153
	 * @since 0.6.5
154
	 * 
155
	 * @param $distance String
156
	 * 
157
	 * @return string
158
	 */
159
	protected static function normalizeDistance( $distance ) {
160
		$distance = trim( (string)$distance );
161
		$strlen = strlen( $distance );
162
		
163
		for ( $i = 0; $i < $strlen; $i++ ) {
164
			if ( !ctype_digit( $distance{$i} ) && !in_array( $distance{$i}, [ ',', '.' ] ) ) {
165
				$value = substr( $distance, 0, $i );
166
				$unit = substr( $distance, $i );
167
				break;
168
			}
169
		}
170
		
171
		$value = str_replace( ',', '.', isset( $value ) ? $value : $distance );
172
		
173
		if ( isset( $unit ) ) {
174
			$value .= ' ' . str_replace( [ ' ', "\t" ], '', $unit );
175
		}
176
177
		return $value;
178
	}
179
	
180
	private static function initUnitRegex() {
181
		if ( self::$unitRegex === false ) {
182
			global $egMapsDistanceUnits;
183
			self::$unitRegex = implode( '|', array_keys( $egMapsDistanceUnits ) ) . '|';
0 ignored issues
show
Documentation Bug introduced by
The property $unitRegex was declared of type boolean, but implode('|', array_keys(...psDistanceUnits)) . '|' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
184
		}		
185
	}
186
	
187
}