Index: trunk/phase3/tests/phpunit/includes/PathRouterTest.php |
— | — | @@ -5,13 +5,17 @@ |
6 | 6 | |
7 | 7 | class PathRouterTest extends MediaWikiTestCase { |
8 | 8 | |
| 9 | + public function setUp() { |
| 10 | + $router = new PathRouter; |
| 11 | + $router->add("/wiki/$1"); |
| 12 | + $this->basicRouter = $router; |
| 13 | + } |
| 14 | + |
9 | 15 | /** |
10 | 16 | * Test basic path parsing |
11 | 17 | */ |
12 | 18 | public function testBasic() { |
13 | | - $router = new PathRouter; |
14 | | - $router->add("/$1"); |
15 | | - $matches = $router->parse( "/Foo" ); |
| 19 | + $matches = $this->basicRouter->parse( "/wiki/Foo" ); |
16 | 20 | $this->assertEquals( $matches, array( 'title' => "Foo" ) ); |
17 | 21 | } |
18 | 22 | |
— | — | @@ -111,7 +115,7 @@ |
112 | 116 | array( 'a' => 'b', 'data:foo' => 'bar' ), |
113 | 117 | array( 'callback' => array( $this, 'callbackForTest' ) ) |
114 | 118 | ); |
115 | | - $matches = $router->parse( '/Foo'); |
| 119 | + $matches = $router->parse( '/Foo' ); |
116 | 120 | $this->assertEquals( $matches, array( |
117 | 121 | 'title' => "Foo", |
118 | 122 | 'x' => 'Foo', |
— | — | @@ -152,4 +156,50 @@ |
153 | 157 | } |
154 | 158 | } |
155 | 159 | |
| 160 | + /** |
| 161 | + * Make sure the router handles titles like Special:Recentchanges correctly |
| 162 | + */ |
| 163 | + public function testSpecial() { |
| 164 | + $matches = $this->basicRouter->parse( "/wiki/Special:Recentchanges" ); |
| 165 | + $this->assertEquals( $matches, array( 'title' => "Special:Recentchanges" ) ); |
| 166 | + } |
| 167 | + |
| 168 | + /** |
| 169 | + * Make sure the router decodes urlencoding properly |
| 170 | + */ |
| 171 | + public function testUrlencoding() { |
| 172 | + $matches = $this->basicRouter->parse( "/wiki/Title_With%20Space" ); |
| 173 | + $this->assertEquals( $matches, array( 'title' => "Title_With Space" ) ); |
| 174 | + } |
| 175 | + |
| 176 | + /** |
| 177 | + * Make sure the router handles characters like +&() properly |
| 178 | + */ |
| 179 | + public function testCharacters() { |
| 180 | + $matches = $this->basicRouter->parse( "/wiki/Plus+And&Stuff()" ); |
| 181 | + $this->assertEquals( $matches, array( 'title' => "Plus+And&Stuff()" ) ); |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * Make sure the router handles unicode characters correctly |
| 186 | + * @depends testSpecial |
| 187 | + * @depends testUrlencoding |
| 188 | + * @depends testCharacters |
| 189 | + */ |
| 190 | + public function testUnicode() { |
| 191 | + $matches = $this->basicRouter->parse( "/wiki/Spécial:Modifications_récentes" ); |
| 192 | + $this->assertEquals( $matches, array( 'title' => "Spécial:Modifications_récentes" ) ); |
| 193 | + |
| 194 | + $matches = $this->basicRouter->parse( "/wiki/Sp%C3%A9cial:Modifications_r%C3%A9centes" ); |
| 195 | + $this->assertEquals( $matches, array( 'title' => "Spécial:Modifications_récentes" ) ); |
| 196 | + } |
| 197 | + |
| 198 | + /** |
| 199 | + * Ensure the router doesn't choke on long paths. |
| 200 | + */ |
| 201 | + public function testLength() { |
| 202 | + $matches = $this->basicRouter->parse( "/wiki/Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum." ); |
| 203 | + $this->assertEquals( $matches, array( 'title' => "Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum." ) ); |
| 204 | + } |
| 205 | + |
156 | 206 | } |
Index: trunk/phase3/includes/PathRouter.php |
— | — | @@ -73,7 +73,7 @@ |
74 | 74 | |
75 | 75 | foreach ( $params as $paramName => $paramData ) { |
76 | 76 | if ( is_string( $paramData ) ) { |
77 | | - if ( preg_match( '/\$(\d+|key)/', $paramData ) ) { |
| 77 | + if ( preg_match( '/\$(\d+|key)/u', $paramData ) ) { |
78 | 78 | $paramArrKey = 'pattern'; |
79 | 79 | } else { |
80 | 80 | // If there's no replacement use a value instead |
— | — | @@ -87,7 +87,7 @@ |
88 | 88 | } |
89 | 89 | |
90 | 90 | foreach ( $options as $optionName => $optionData ) { |
91 | | - if ( preg_match( '/^\$\d+$/', $optionName ) ) { |
| 91 | + if ( preg_match( '/^\$\d+$/u', $optionName ) ) { |
92 | 92 | if ( !is_array( $optionData ) ) { |
93 | 93 | $options[$optionName] = array( $optionData ); |
94 | 94 | } |
— | — | @@ -147,10 +147,10 @@ |
148 | 148 | |
149 | 149 | # For each level of the path |
150 | 150 | foreach( $path as $piece ) { |
151 | | - if ( preg_match( '/^\$(\d+|key)$/', $piece ) ) { |
| 151 | + if ( preg_match( '/^\$(\d+|key)$/u', $piece ) ) { |
152 | 152 | # For a piece that is only a $1 variable add 1 points of weight |
153 | 153 | $weight += 1; |
154 | | - } elseif ( preg_match( '/\$(\d+|key)/', $piece ) ) { |
| 154 | + } elseif ( preg_match( '/\$(\d+|key)/u', $piece ) ) { |
155 | 155 | # For a piece that simply contains a $1 variable add 2 points of weight |
156 | 156 | $weight += 2; |
157 | 157 | } else { |
— | — | @@ -160,7 +160,7 @@ |
161 | 161 | } |
162 | 162 | |
163 | 163 | foreach ( $pattern->options as $key => $option ) { |
164 | | - if ( preg_match( '/^\$\d+$/', $key ) ) { |
| 164 | + if ( preg_match( '/^\$\d+$/u', $key ) ) { |
165 | 165 | # Add 0.5 for restrictions to values |
166 | 166 | # This way given two separate "/$2/$1" patterns the |
167 | 167 | # one with a limited set of $2 values will dominate |
— | — | @@ -195,8 +195,8 @@ |
196 | 196 | |
197 | 197 | protected static function extractTitle( $path, $pattern ) { |
198 | 198 | $regexp = preg_quote( $pattern->path, '#' ); |
199 | | - $regexp = preg_replace( '#\\\\\$1#', '(?P<par1>.*)', $regexp ); |
200 | | - $regexp = preg_replace( '#\\\\\$(\d+)#', '(?P<par$1>.+?)', $regexp ); |
| 199 | + $regexp = preg_replace( '#\\\\\$1#u', '(?P<par1>.*)', $regexp ); |
| 200 | + $regexp = preg_replace( '#\\\\\$(\d+)#u', '(?P<par$1>.+?)', $regexp ); |
201 | 201 | $regexp = "#^{$regexp}$#"; |
202 | 202 | |
203 | 203 | $matches = array(); |
— | — | @@ -204,9 +204,9 @@ |
205 | 205 | |
206 | 206 | if ( preg_match( $regexp, $path, $m ) ) { |
207 | 207 | foreach ( $pattern->options as $key => $option ) { |
208 | | - if ( preg_match( '/^\$\d+$/', $key ) ) { |
| 208 | + if ( preg_match( '/^\$\d+$/u', $key ) ) { |
209 | 209 | $n = intval( substr( $key, 1 ) ); |
210 | | - $value = $m["par{$n}"]; |
| 210 | + $value = rawurldecode( $m["par{$n}"] ); |
211 | 211 | if ( !in_array( $value, $option ) ) { |
212 | 212 | return null; |
213 | 213 | } |
— | — | @@ -214,9 +214,9 @@ |
215 | 215 | } |
216 | 216 | |
217 | 217 | foreach ( $m as $matchKey => $matchValue ) { |
218 | | - if ( preg_match( '/^par\d+$/', $matchKey ) ) { |
| 218 | + if ( preg_match( '/^par\d+$/u', $matchKey ) ) { |
219 | 219 | $n = intval( substr( $matchKey, 3 ) ); |
220 | | - $data['$'.$n] = $matchValue; |
| 220 | + $data['$'.$n] = rawurldecode( $matchValue ); |
221 | 221 | } |
222 | 222 | } |
223 | 223 | if ( isset( $pattern->key ) ) { |
— | — | @@ -225,7 +225,7 @@ |
226 | 226 | |
227 | 227 | foreach ( $pattern->params as $paramName => $paramData ) { |
228 | 228 | $value = null; |
229 | | - if ( preg_match( '/^data:/', $paramName ) ) { |
| 229 | + if ( preg_match( '/^data:/u', $paramName ) ) { |
230 | 230 | $isData = true; |
231 | 231 | $key = substr( $paramName, 5 ); |
232 | 232 | } else { |
— | — | @@ -238,15 +238,15 @@ |
239 | 239 | } elseif ( isset( $paramData['pattern'] ) ) { |
240 | 240 | $value = $paramData['pattern']; |
241 | 241 | foreach ( $m as $matchKey => $matchValue ) { |
242 | | - if ( preg_match( '/^par\d+$/', $matchKey ) ) { |
| 242 | + if ( preg_match( '/^par\d+$/u', $matchKey ) ) { |
243 | 243 | $n = intval( substr( $matchKey, 3 ) ); |
244 | | - $value = str_replace( '$' . $n, $matchValue, $value ); |
| 244 | + $value = str_replace( '$' . $n, rawurldecode( $matchValue ), $value ); |
245 | 245 | } |
246 | 246 | } |
247 | 247 | if ( isset( $pattern->key ) ) { |
248 | 248 | $value = str_replace( '$key', $pattern->key, $value ); |
249 | 249 | } |
250 | | - if ( preg_match( '/\$(\d+|key)/', $value ) ) { |
| 250 | + if ( preg_match( '/\$(\d+|key)/u', $value ) ) { |
251 | 251 | // Still contains $# or $key patterns after replacement |
252 | 252 | // Seams like we don't have all the data, abort |
253 | 253 | return null; |