Index: trunk/phase3/RELEASE-NOTES-1.19 |
— | — | @@ -125,6 +125,7 @@ |
126 | 126 | from recent changes feeds |
127 | 127 | * (bug 30232) add current time to message wlnote on Special:Watchlist |
128 | 128 | * (bug 29110) $wgFeedDiffCutoff did not affect new pages |
| 129 | +* (bug 32168) Add wfRemoveDotSegments for use in wfExpandUrl |
129 | 130 | |
130 | 131 | === API changes in 1.19 === |
131 | 132 | * (bug 19838) siprop=interwikimap can now use the interwiki cache. |
Index: trunk/phase3/tests/phpunit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php |
— | — | @@ -0,0 +1,86 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Unit tests for wfRemoveDotSegments() |
| 5 | + */ |
| 6 | + |
| 7 | +class wfRemoveDotSegments extends MediaWikiTestCase { |
| 8 | + /** @dataProvider providePaths */ |
| 9 | + public function testWfRemoveDotSegments( $inputPath, $outputPath ) { |
| 10 | + $actualPath = wfRemoveDotSegments( $inputPath ); |
| 11 | + $message = "Testing $inputPath expands to $outputPath"; |
| 12 | + echo $message . "\n"; |
| 13 | + $this->assertEquals( $outputPath, $actualPath, $message ); |
| 14 | + } |
| 15 | + |
| 16 | + /** |
| 17 | + * Provider of URL paths for testing wfRemoveDotSegments() |
| 18 | + * |
| 19 | + * @return array |
| 20 | + */ |
| 21 | + public function providePaths() { |
| 22 | + return array( |
| 23 | + array( '/a/b/c/./../../g', '/a/g' ), |
| 24 | + array( 'mid/content=5/../6', 'mid/6' ), |
| 25 | + array( '/a//../b', '/a/b' ), |
| 26 | + array( '', '' ), |
| 27 | + array( '/', '/' ), |
| 28 | + array( '//', '//' ), |
| 29 | + array( '.', '' ), |
| 30 | + array( '..', '' ), |
| 31 | + array( '/.', '/' ), |
| 32 | + array( '/..', '/' ), |
| 33 | + array( './', '' ), |
| 34 | + array( '../', '' ), |
| 35 | + array( './a', 'a' ), |
| 36 | + array( '../a', 'a' ), |
| 37 | + array( '../../a', 'a' ), |
| 38 | + array( '.././a', 'a' ), |
| 39 | + array( './../a', 'a' ), |
| 40 | + array( '././a', 'a' ), |
| 41 | + array( '../../', '' ), |
| 42 | + array( '.././', '' ), |
| 43 | + array( './../', '' ), |
| 44 | + array( '././', '' ), |
| 45 | + array( '../..', '' ), |
| 46 | + array( '../.', '' ), |
| 47 | + array( './..', '' ), |
| 48 | + array( './.', '' ), |
| 49 | + array( '/../../a', '/a' ), |
| 50 | + array( '/.././a', '/a' ), |
| 51 | + array( '/./../a', '/a' ), |
| 52 | + array( '/././a', '/a' ), |
| 53 | + array( '/../../', '/' ), |
| 54 | + array( '/.././', '/' ), |
| 55 | + array( '/./../', '/' ), |
| 56 | + array( '/././', '/' ), |
| 57 | + array( '/../..', '/' ), |
| 58 | + array( '/../.', '/' ), |
| 59 | + array( '/./..', '/' ), |
| 60 | + array( '/./.', '/' ), |
| 61 | + array( 'b/../../a', '/a' ), |
| 62 | + array( 'b/.././a', '/a' ), |
| 63 | + array( 'b/./../a', '/a' ), |
| 64 | + array( 'b/././a', 'b/a' ), |
| 65 | + array( 'b/../../', '/' ), |
| 66 | + array( 'b/.././', '/' ), |
| 67 | + array( 'b/./../', '/' ), |
| 68 | + array( 'b/././', 'b/' ), |
| 69 | + array( 'b/../..', '/' ), |
| 70 | + array( 'b/../.', '/' ), |
| 71 | + array( 'b/./..', '/' ), |
| 72 | + array( 'b/./.', 'b/' ), |
| 73 | + array( '/b/../../a', '/a' ), |
| 74 | + array( '/b/.././a', '/a' ), |
| 75 | + array( '/b/./../a', '/a' ), |
| 76 | + array( '/b/././a', '/b/a' ), |
| 77 | + array( '/b/../../', '/' ), |
| 78 | + array( '/b/.././', '/' ), |
| 79 | + array( '/b/./../', '/' ), |
| 80 | + array( '/b/././', '/b/' ), |
| 81 | + array( '/b/../..', '/' ), |
| 82 | + array( '/b/../.', '/' ), |
| 83 | + array( '/b/./..', '/' ), |
| 84 | + array( '/b/./.', '/b/' ), |
| 85 | + ); |
| 86 | + } |
| 87 | +} |
Index: trunk/phase3/includes/GlobalFunctions.php |
— | — | @@ -477,9 +477,9 @@ |
478 | 478 | |
479 | 479 | $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 ); |
480 | 480 | |
481 | | - if( substr( $url, 0, 2 ) == '//' ) { |
| 481 | + if ( substr( $url, 0, 2 ) == '//' ) { |
482 | 482 | return $defaultProtoWithoutSlashes . $url; |
483 | | - } elseif( substr( $url, 0, 1 ) == '/' ) { |
| 483 | + } elseif ( substr( $url, 0, 1 ) == '/' ) { |
484 | 484 | // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, otherwise leave it alone |
485 | 485 | return ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url; |
486 | 486 | } else { |
— | — | @@ -488,6 +488,47 @@ |
489 | 489 | } |
490 | 490 | |
491 | 491 | /** |
| 492 | + * Remove all dot-segments in the provided URL path. For example, |
| 493 | + * '/a/./b/../c/' becomes '/a/c/'. For details on the algorithm, please see |
| 494 | + * RFC3986 section 5.2.4. |
| 495 | + * |
| 496 | + * @todo Need to integrate this into wfExpandUrl (bug 32168) |
| 497 | + * |
| 498 | + * @param $urlPath String URL path, potentially containing dot-segments |
| 499 | + * @return string URL path with all dot-segments removed |
| 500 | + */ |
| 501 | +function wfRemoveDotSegments( $urlPath ) { |
| 502 | + $output = ''; |
| 503 | + |
| 504 | + while ( $urlPath ) { |
| 505 | + $matches = null; |
| 506 | + if ( preg_match('%^\.\.?/%', $urlPath, $matches) ) { |
| 507 | + # Step A |
| 508 | + $urlPath = substr( $urlPath, strlen( $matches[0] ) ); |
| 509 | + } elseif ( preg_match( '%^/\.(/|$)%', $urlPath, $matches ) ) { |
| 510 | + # Step B |
| 511 | + $start = strlen( $matches[0] ); |
| 512 | + $urlPath = '/' . substr( $urlPath, $start ); |
| 513 | + } elseif ( preg_match( '%^/\.\.(/|$)%', $urlPath, $matches ) ) { |
| 514 | + # Step C |
| 515 | + $start = strlen( $matches[0] ); |
| 516 | + $urlPath = '/' . substr( $urlPath, $start ); |
| 517 | + $output = preg_replace('%(^|/)[^/]*$%', '', $output); |
| 518 | + } elseif ( preg_match( '%^\.\.?$%', $urlPath, $matches ) ) { |
| 519 | + # Step D |
| 520 | + $urlPath = substr( $urlPath, strlen( $matches[0] ) ); |
| 521 | + } else { |
| 522 | + # Step E |
| 523 | + preg_match( '%^/?[^/]*%', $urlPath, $matches ); |
| 524 | + $urlPath = substr( $urlPath, strlen( $matches[0] ) ); |
| 525 | + $output .= $matches[0]; |
| 526 | + } |
| 527 | + } |
| 528 | + |
| 529 | + return $output; |
| 530 | +} |
| 531 | + |
| 532 | +/** |
492 | 533 | * Returns a regular expression of url protocols |
493 | 534 | * |
494 | 535 | * @param $includeProtocolRelative bool If false, remove '//' from the returned protocol list. |