Make WordPress Core

Ticket #36356: 36356.2.diff

File 36356.2.diff, 11.5 KB (added by peterwilsoncc, 8 years ago)
  • src/wp-includes/http.php

    diff --git src/wp-includes/http.php src/wp-includes/http.php
    index 3738b79..96c6b75 100644
    function ms_allowed_http_request_hosts( $is_external, $host ) { 
    623623}
    624624
    625625/**
    626  * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7
     626 * A wrapper for PHP's parse_url() function that handles consistency in the return
     627 * values across PHP versions.
    627628 *
    628629 * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including
    629  * schemeless and relative url's with :// in the path, this works around those
    630  * limitations providing a standard output on PHP 5.2~5.4+.
     630 * schemeless and relative url's with :// in the path. This function works around
     631 * those limitations providing a standard output on PHP 5.2~5.4+.
     632 *
     633 * Secondly, across various PHP versions, schemeless URLs starting containing a ":"
     634 * in the query are being handled inconsistently. This function works around those
     635 * differences as well.
    631636 *
    632637 * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated
    633638 * when URL parsing failed.
    function ms_allowed_http_request_hosts( $is_external, $host ) { 
    640645 *                          predefined constants to specify which one.
    641646 *                          Defaults to -1 (= return all parts as an array).
    642647 *                          @see http://php.net/manual/en/function.parse-url.php
    643  * @return mixed False on failure; Array of URL components on success;
    644  *               When a specific component has been requested: null if the component doesn't
    645  *               exist in the given URL; a sting or - in the case of PHP_URL_PORT - integer
    646  *               when it does; See parse_url()'s return values.
     648 * @return mixed False on parse failure; Array of URL components on success;
     649 *               When a specific component has been requested: null if the component
     650 *               doesn't exist in the given URL; a sting or - in the case of
     651 *               PHP_URL_PORT - integer when it does. See parse_url()'s return values.
    647652 */
    648653function wp_parse_url( $url, $component = -1 ) {
    649         $parts = @parse_url( $url, $component );
     654        $to_unset = array();
     655        $url = strval( $url );
     656
     657        if ( '//' === substr( $url, 0, 2 ) ) {
     658                $to_unset[] = 'scheme';
     659                $url = 'placeholder:' . $url;
     660        } elseif ( '/' === substr( $url, 0, 1 ) ) {
     661                $to_unset[] = 'scheme';
     662                $to_unset[] = 'host';
     663                $url = 'placeholder://placeholder' . $url;
     664        }
     665
     666        $parts = @parse_url( $url );
    650667
    651         if ( version_compare( PHP_VERSION, '5.4.7', '>=' ) ) {
    652                 return $parts;
     668        // Remove the placeholder values.
     669        foreach ( $to_unset as $key ) {
     670                unset( $parts[ $key ] );
    653671        }
    654672
    655         if ( false === $parts ) {
    656                 // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path.
    657                 if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) {
    658                         if ( in_array( $component, array( PHP_URL_SCHEME, PHP_URL_HOST ), true ) ) {
    659                                 return null;
    660                         }
    661                         // Since we know it's a relative path, prefix with a scheme/host placeholder and try again.
    662                         if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url, $component ) ) {
    663                                 return $parts;
    664                         }
    665                         // Remove the placeholder values.
    666                         if ( -1 === $component ) {
    667                                 unset( $parts['scheme'], $parts['host'] );
    668                         }
    669                 } else {
    670                         return $parts;
    671                 }
     673        return _get_component_from_parsed_url_array( $parts, $component );
     674}
     675
     676/**
     677 * Retrieve a specific component from a parsed URL array.
     678 *
     679 * @internal
     680 *
     681 * @since 4.7.0
     682 *
     683 * @param array|false $url_parts The parsed URL. Can be false if the URL failed to parse.
     684 * @param int    $component The specific component to retrieve. Use one of the PHP
     685 *                          predefined constants to specify which one.
     686 *                          Defaults to -1 (= return all parts as an array).
     687 *                          @see http://php.net/manual/en/function.parse-url.php
     688 * @return mixed False on parse failure; Array of URL components on success;
     689 *               When a specific component has been requested: null if the component
     690 *               doesn't exist in the given URL; a sting or - in the case of
     691 *               PHP_URL_PORT - integer when it does. See parse_url()'s return values.
     692 */
     693function _get_component_from_parsed_url_array( $url_parts, $component = -1 ) {
     694        if ( -1 === $component ) {
     695                return $url_parts;
    672696        }
    673697
    674         // < PHP 5.4.7 compat, doesn't detect a schemeless URL's host field.
    675         if ( '//' == substr( $url, 0, 2 ) ) {
    676                 if ( -1 === $component && ! isset( $parts['host'] ) ) {
    677                         $path_parts = explode( '/', substr( $parts['path'], 2 ), 2 );
    678                         $parts['host'] = $path_parts[0];
    679                         if ( isset( $path_parts[1] ) ) {
    680                                 $parts['path'] = '/' . $path_parts[1];
    681                         } else {
    682                                 unset( $parts['path'] );
    683                         }
    684                 } elseif ( PHP_URL_HOST === $component || PHP_URL_PATH === $component ) {
    685                         $all_parts = @parse_url( $url );
    686                         if ( ! isset( $all_parts['host'] ) ) {
    687                                 $path_parts = explode( '/', substr( $all_parts['path'], 2 ), 2 );
    688                                 if ( PHP_URL_PATH === $component ) {
    689                                         if ( isset( $path_parts[1] ) ) {
    690                                                 $parts = '/' . $path_parts[1];
    691                                         } else {
    692                                                 $parts = null;
    693                                         }
    694                                 } elseif ( PHP_URL_HOST === $component ) {
    695                                         $parts = $path_parts[0];
    696                                 }
    697                         }
    698                 }
     698        $key = _wp_translate_php_url_constant_to_key( $component );
     699        if ( false !== $key && is_array( $url_parts ) && isset( $url_parts[ $key ] ) ) {
     700                return $url_parts[ $key ];
     701        } else {
     702                return null;
    699703        }
     704}
    700705
    701         return $parts;
     706/**
     707 * Translate a PHP_URL_* constant to the named array keys PHP uses.
     708 *
     709 * @internal
     710 *
     711 * @since 4.7.0
     712 *
     713 * @see   http://php.net/manual/en/url.constants.php
     714 *
     715 * @param int $constant PHP_URL_* constant.
     716 * @return string|bool The named key or false.
     717 */
     718function _wp_translate_php_url_constant_to_key( $constant ) {
     719        $translation = array(
     720                PHP_URL_SCHEME   => 'scheme',
     721                PHP_URL_HOST     => 'host',
     722                PHP_URL_PORT     => 'port',
     723                PHP_URL_USER     => 'user',
     724                PHP_URL_PASS     => 'pass',
     725                PHP_URL_PATH     => 'path',
     726                PHP_URL_QUERY    => 'query',
     727                PHP_URL_FRAGMENT => 'fragment',
     728        );
     729
     730        if ( isset( $translation[ $constant ] ) ) {
     731                return $translation[ $constant ];
     732        } else {
     733                return false;
     734        }
    702735}
  • tests/phpunit/tests/http/http.php

    diff --git tests/phpunit/tests/http/http.php tests/phpunit/tests/http/http.php
    index 607eb47..bcd0950 100644
    class Tests_HTTP_HTTP extends WP_UnitTestCase { 
    107107                        // PHP's parse_url() calls this an invalid url, we handle it as a path
    108108                        array( '/://example.com/', array( 'path' => '/://example.com/' ) ),
    109109
     110                        // Schemeless URL containing colons cause parse errors in PHP 7+.
     111                        array(
     112                                '//fonts.googleapis.com/css?family=Open+Sans:400&subset=latin',
     113                                array(
     114                                        'host'  => 'fonts.googleapis.com',
     115                                        'path'  => '/css',
     116                                        'query' => 'family=Open+Sans:400&subset=latin',
     117                                ),
     118                        ),
     119                        array(
     120                                '//fonts.googleapis.com/css?family=Open+Sans:400',
     121                                array(
     122                                        'host'  => 'fonts.googleapis.com',
     123                                        'path'  => '/css',
     124                                        'query' => 'family=Open+Sans:400',
     125                                ),
     126                        ),
     127
     128                        // Query parameter starting with & instead of ?
     129                        array(
     130                                'http://www.test.com/path1/path2/&q=a',
     131                                array(
     132                                        'scheme' => 'http',
     133                                        'host'   => 'www.test.com',
     134                                        'path'   => '/path1/path2/&q=a',
     135                                ),
     136                        ),
     137                        array(
     138                                'http://www.test.com/path1/path2/file.php&q=a',
     139                                array(
     140                                        'scheme' => 'http',
     141                                        'host'  => 'www.test.com',
     142                                        'path'  => '/path1/path2/file.php&q=a',
     143                                ),
     144                        ),
     145
     146                        array( 'filenamefound', array( 'path' => 'filenamefound' ) ),
     147
     148                        // Empty string or non-string passed in.
     149                        array( '', array( 'path' => '' ) ),
     150                        array( 123, array( 'path' => '123' ) ),
    110151                );
    111152                /*
    112153                Untestable edge cases in various PHP:
    class Tests_HTTP_HTTP extends WP_UnitTestCase { 
    117158
    118159        /**
    119160         * @ticket 36356
    120     */
     161        */
    121162        function test_wp_parse_url_with_default_component() {
    122163                $actual = wp_parse_url( self::FULL_TEST_URL, -1 );
    123164                $this->assertEquals( array(
    class Tests_HTTP_HTTP extends WP_UnitTestCase { 
    175216                        // PHP's parse_url() calls this an invalid URL, we handle it as a path.
    176217                        array( '/://example.com/', PHP_URL_PATH, '/://example.com/' ),
    177218
     219                        // Schemeless URL containing colons cause parse errors in PHP 7+.
     220                        array( '//fonts.googleapis.com/css?family=Open+Sans:400&subset=latin', PHP_URL_HOST, 'fonts.googleapis.com' ),
     221                        array( '//fonts.googleapis.com/css?family=Open+Sans:400&subset=latin', PHP_URL_PORT, null ),
     222                        array( '//fonts.googleapis.com/css?family=Open+Sans:400&subset=latin', PHP_URL_PATH, '/css' ),
     223                        array( '//fonts.googleapis.com/css?family=Open+Sans:400&subset=latin', PHP_URL_QUERY, 'family=Open+Sans:400&subset=latin' ),
     224                        array( '//fonts.googleapis.com/css?family=Open+Sans:400', PHP_URL_HOST, 'fonts.googleapis.com' ), // 25
     225                        array( '//fonts.googleapis.com/css?family=Open+Sans:400', PHP_URL_PORT, null ),
     226                        array( '//fonts.googleapis.com/css?family=Open+Sans:400', PHP_URL_PATH, '/css' ), //27
     227                        array( '//fonts.googleapis.com/css?family=Open+Sans:400', PHP_URL_QUERY, 'family=Open+Sans:400' ), //28
     228
     229                        // Query parameter starting with & instead of ?
     230                        array( 'http://www.test.com/path1/path2/&q=a', PHP_URL_PATH, '/path1/path2/&q=a' ),
     231                        array( 'http://www.test.com/path1/path2/&q=a', PHP_URL_QUERY, null ),
     232                        array( 'http://www.test.com/path1/path2/file.php&q=a', PHP_URL_PATH, '/path1/path2/file.php&q=a' ),
     233                        array( 'http://www.test.com/path1/path2/file.php&q=a', PHP_URL_QUERY, null ),
     234
     235                        // Empty string or non-string passed in.
     236                        array( '', PHP_URL_PATH, '' ),
     237                        array( '', PHP_URL_QUERY, null ),
     238                        array( 123, PHP_URL_PORT, null ),
     239                        array( 123, PHP_URL_PATH, '123' ),
    178240                );
    179241        }
    180242
    class Tests_HTTP_HTTP extends WP_UnitTestCase { 
    224286                        }
    225287                }
    226288        }
     289
     290        /**
     291         * @ticket 36356
     292         *
     293         * @dataProvider get_component_from_parsed_url_array_testcases
     294         */
     295        function test_get_component_from_parsed_url_array( $url, $component, $expected ) {
     296                $parts  = wp_parse_url( $url );
     297                $actual = _get_component_from_parsed_url_array( $parts, $component );
     298                $this->assertSame( $expected, $actual );
     299        }
     300
     301        function get_component_from_parsed_url_array_testcases() {
     302                // 0: A URL, 1: PHP URL constant, 2: The expected result.
     303                return array(
     304                        array( 'http://example.com/', -1, array( 'scheme' => 'http', 'host' => 'example.com', 'path' => '/' ) ),
     305                        array( 'http://example.com/', -1, array( 'scheme' => 'http', 'host' => 'example.com', 'path' => '/' ) ),
     306                        array( 'http://example.com/', PHP_URL_HOST, 'example.com' ),
     307                        array( 'http://example.com/', PHP_URL_USER, null ),
     308                        array( 'http:///example.com', -1, false ), // Malformed.
     309                        array( 'http:///example.com', PHP_URL_HOST, null ), // Malformed.
     310                );
     311        }
     312
     313        /**
     314         * @ticket 36356
     315         *
     316         * @dataProvider wp_translate_php_url_constant_to_key_testcases
     317         */
     318        function test_wp_translate_php_url_constant_to_key( $input, $expected ) {
     319                $actual = _wp_translate_php_url_constant_to_key( $input );
     320                $this->assertSame( $expected, $actual );
     321        }
     322
     323        function wp_translate_php_url_constant_to_key_testcases() {
     324                // 0: PHP URL constant, 1: The expected result.
     325                return array(
     326                        array( PHP_URL_SCHEME, 'scheme' ),
     327                        array( PHP_URL_HOST, 'host' ),
     328                        array( PHP_URL_PORT, 'port' ),
     329                        array( PHP_URL_USER, 'user' ),
     330                        array( PHP_URL_PASS, 'pass' ),
     331                        array( PHP_URL_PATH, 'path' ),
     332                        array( PHP_URL_QUERY, 'query' ),
     333                        array( PHP_URL_FRAGMENT, 'fragment' ),
     334
     335                        // Test with non-PHP_URL_CONSTANT parameter.
     336                        array( 'something', false ),
     337                        array( ABSPATH, false ),
     338                );
     339        }
     340
    227341}