Completed
Push — master ( 684184...9a35aa )
by Jeroen De
08:07
created

includes/Maps_DistanceParser.php (1 issue)

Check for loose comparison of strings.

Bug Major

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 );
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 ) ) . '|';
184
		}		
185
	}
186
	
187
}