These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace DataValues\Geo\Parsers; |
||
4 | |||
5 | use DataValues\Geo\Values\LatLongValue; |
||
6 | use ValueParsers\ParseException; |
||
7 | use ValueParsers\ParserOptions; |
||
8 | |||
9 | /** |
||
10 | * Parser for geographical coordinates in Decimal Degree notation. |
||
11 | * |
||
12 | * @since 0.1 |
||
13 | * |
||
14 | * @license GPL-2.0+ |
||
15 | * @author Jeroen De Dauw < [email protected] > |
||
16 | * @author H. Snater < [email protected] > |
||
17 | */ |
||
18 | class DdCoordinateParser extends LatLongParserBase { |
||
19 | |||
20 | /** |
||
21 | * The symbol representing degrees. |
||
22 | * @since 0.1 |
||
23 | */ |
||
24 | const OPT_DEGREE_SYMBOL = 'degree'; |
||
25 | |||
26 | /** |
||
27 | * @param ParserOptions|null $options |
||
28 | */ |
||
29 | 21 | public function __construct( ParserOptions $options = null ) { |
|
30 | 21 | parent::__construct( $options ); |
|
31 | |||
32 | 21 | $this->options->defaultOption( self::OPT_DEGREE_SYMBOL, '°' ); |
|
33 | |||
34 | 21 | $this->defaultDelimiters = [ $this->getOption( self::OPT_DEGREE_SYMBOL ) ]; |
|
0 ignored issues
–
show
|
|||
35 | 21 | } |
|
36 | |||
37 | /** |
||
38 | * @see LatLongParserBase::getParsedCoordinate |
||
39 | * |
||
40 | * @param string $coordinateSegment |
||
41 | * |
||
42 | * @return float |
||
43 | */ |
||
44 | 16 | protected function getParsedCoordinate( $coordinateSegment ) { |
|
45 | 16 | $coordinateSegment = $this->resolveDirection( $coordinateSegment ); |
|
46 | 16 | return $this->parseCoordinate( $coordinateSegment ); |
|
47 | } |
||
48 | |||
49 | /** |
||
50 | * @see LatLongParserBase::areValidCoordinates |
||
51 | * |
||
52 | * @param string[] $normalizedCoordinateSegments |
||
53 | * |
||
54 | * @return bool |
||
55 | */ |
||
56 | 21 | protected function areValidCoordinates( array $normalizedCoordinateSegments ) { |
|
57 | // TODO: Implement localized decimal separator. |
||
58 | 21 | $baseRegExp = '\d{1,3}(\.\d{1,20})?' . $this->getOption( self::OPT_DEGREE_SYMBOL ); |
|
59 | |||
60 | // Cache whether the coordinates are specified in directional format (a mixture of |
||
61 | // directional and non-directional is regarded invalid). |
||
62 | 21 | $directional = false; |
|
63 | |||
64 | 21 | $match = false; |
|
65 | |||
66 | 21 | foreach ( $normalizedCoordinateSegments as $i => $segment ) { |
|
67 | $direction = '(' |
||
68 | 21 | . $this->getOption( self::OPT_NORTH_SYMBOL ) . '|' |
|
69 | 21 | . $this->getOption( self::OPT_SOUTH_SYMBOL ) . ')'; |
|
70 | |||
71 | 21 | if ( $i === 1 ) { |
|
72 | $direction = '(' |
||
73 | 16 | . $this->getOption( self::OPT_EAST_SYMBOL ) . '|' |
|
74 | 16 | . $this->getOption( self::OPT_WEST_SYMBOL ) . ')'; |
|
75 | } |
||
76 | |||
77 | 21 | $match = preg_match( |
|
78 | 21 | '/^(' . $baseRegExp . $direction . '|' . $direction . $baseRegExp . ')$/i', |
|
79 | 21 | $segment |
|
80 | ); |
||
81 | |||
82 | 21 | if ( $directional ) { |
|
83 | // Directionality is only set after parsing latitude: When the latitude is |
||
84 | // is directional, the longitude needs to be as well. Therefore we break here since |
||
85 | // checking for directionality is the only check needed for longitude. |
||
86 | 10 | break; |
|
87 | 21 | } elseif ( $match ) { |
|
88 | // Latitude is directional, no need to check for non-directionality. |
||
89 | 10 | $directional = true; |
|
90 | 10 | continue; |
|
91 | } |
||
92 | |||
93 | 11 | $match = preg_match( '/^(-)?' . $baseRegExp . '$/i', $segment ); |
|
94 | |||
95 | 11 | if ( !$match ) { |
|
96 | // Does neither match directional nor non-directional. |
||
97 | 11 | break; |
|
98 | } |
||
99 | } |
||
100 | |||
101 | 21 | return ( 1 === $match ); |
|
102 | } |
||
103 | |||
104 | /** |
||
105 | * @see ValueParser::parse |
||
106 | * |
||
107 | * @param string $value |
||
108 | * |
||
109 | * @throws ParseException |
||
110 | * @return LatLongValue |
||
111 | */ |
||
112 | 21 | public function parse( $value ) { |
|
113 | 21 | return parent::parse( $this->getNormalizedNotation( $value ) ); |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * Returns a normalized version of the coordinate string. |
||
118 | * |
||
119 | * @param string $coordinates |
||
120 | * |
||
121 | * @return string |
||
122 | */ |
||
123 | 21 | protected function getNormalizedNotation( $coordinates ) { |
|
124 | 21 | $coordinates = str_replace( |
|
125 | 21 | [ '°', '°' ], |
|
126 | 21 | $this->getOption( self::OPT_DEGREE_SYMBOL ), $coordinates |
|
127 | ); |
||
128 | |||
129 | 21 | $coordinates = $this->removeInvalidChars( $coordinates ); |
|
130 | |||
131 | 21 | return $coordinates; |
|
132 | } |
||
133 | |||
134 | /** |
||
135 | * Returns a string with whitespace, control characters and characters with ASCII values above |
||
136 | * 126 removed. |
||
137 | * |
||
138 | * @see LatLongParserBase::removeInvalidChars |
||
139 | * |
||
140 | * @param string $string |
||
141 | * |
||
142 | * @return string |
||
143 | */ |
||
144 | 21 | protected function removeInvalidChars( $string ) { |
|
145 | 21 | return str_replace( ' ', '', parent::removeInvalidChars( $string ) ); |
|
146 | } |
||
147 | |||
148 | /** |
||
149 | * Converts a coordinate segment to float representation. |
||
150 | * |
||
151 | * @param string $coordinateSegment |
||
152 | * |
||
153 | * @return float |
||
154 | */ |
||
155 | 16 | protected function parseCoordinate( $coordinateSegment ) { |
|
156 | 16 | return (float)str_replace( |
|
157 | 16 | $this->getOption( self::OPT_DEGREE_SYMBOL ), |
|
158 | 16 | '', |
|
159 | 16 | $coordinateSegment |
|
160 | ); |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * @see LatLongParserBase::splitString |
||
165 | * |
||
166 | * @param string $normalizedCoordinateString |
||
167 | * |
||
168 | * @return string[] |
||
169 | */ |
||
170 | 21 | protected function splitString( $normalizedCoordinateString ) { |
|
171 | 21 | $separator = $this->getOption( self::OPT_SEPARATOR_SYMBOL ); |
|
172 | |||
173 | 21 | $normalizedCoordinateSegments = explode( $separator, $normalizedCoordinateString ); |
|
174 | |||
175 | 21 | if ( count( $normalizedCoordinateSegments ) !== 2 ) { |
|
176 | // Separator not present within the string, trying to figure out the segments by |
||
177 | // splitting after the first direction character or degree symbol: |
||
178 | 15 | $delimiters = $this->defaultDelimiters; |
|
179 | |||
180 | $ns = [ |
||
181 | 15 | $this->getOption( self::OPT_NORTH_SYMBOL ), |
|
182 | 15 | $this->getOption( self::OPT_SOUTH_SYMBOL ) |
|
183 | ]; |
||
184 | |||
185 | $ew = [ |
||
186 | 15 | $this->getOption( self::OPT_EAST_SYMBOL ), |
|
187 | 15 | $this->getOption( self::OPT_WEST_SYMBOL ) |
|
188 | ]; |
||
189 | |||
190 | 15 | foreach ( $ns as $delimiter ) { |
|
191 | 15 | if ( mb_strpos( $normalizedCoordinateString, $delimiter ) === 0 ) { |
|
192 | // String starts with "north" or "west" symbol: Separation needs to be done |
||
193 | // before the "east" or "west" symbol. |
||
194 | 2 | $delimiters = array_merge( $ew, $delimiters ); |
|
195 | 15 | break; |
|
196 | } |
||
197 | } |
||
198 | |||
199 | 15 | if ( count( $delimiters ) !== count( $this->defaultDelimiters ) + 2 ) { |
|
200 | 13 | $delimiters = array_merge( $ns, $delimiters ); |
|
201 | } |
||
202 | |||
203 | 15 | foreach ( $delimiters as $delimiter ) { |
|
204 | 15 | $delimiterPos = mb_strpos( $normalizedCoordinateString, $delimiter ); |
|
205 | 15 | if ( $delimiterPos !== false ) { |
|
206 | 10 | $adjustPos = ( in_array( $delimiter, $ew ) ) ? 0 : mb_strlen( $delimiter ); |
|
207 | $normalizedCoordinateSegments = [ |
||
208 | 10 | mb_substr( $normalizedCoordinateString, 0, $delimiterPos + $adjustPos ), |
|
209 | 10 | mb_substr( $normalizedCoordinateString, $delimiterPos + $adjustPos ) |
|
210 | ]; |
||
211 | 15 | break; |
|
212 | } |
||
213 | } |
||
214 | } |
||
215 | |||
216 | 21 | return $normalizedCoordinateSegments; |
|
217 | } |
||
218 | |||
219 | } |
||
220 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..