Make WordPress Core

Changeset 38694


Ignore:
Timestamp:
09/30/2016 09:46:43 PM (7 years ago)
Author:
johnbillion
Message:

HTTP API: Add a $component parameter to wp_parse_url() to give it parity with PHP's parse_url() function.

Fixes #36356
Props jrf

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/http.php

    r38456 r38694  
    634634 *
    635635 * @since 4.4.0
    636  *
    637  * @param string $url The URL to parse.
    638  * @return bool|array False on failure; Array of URL components on success;
    639  *                    See parse_url()'s return values.
    640  */
    641 function wp_parse_url( $url ) {
    642     $parts = @parse_url( $url );
    643     if ( ! $parts ) {
    644         // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path
     636 * @since 4.7.0 The $component parameter was added for parity with PHP's parse_url().
     637 *
     638 * @param string $url       The URL to parse.
     639 * @param int    $component The specific component to retrieve. Use one of the PHP
     640 *                          predefined constants to specify which one.
     641 *                          Defaults to -1 (= return all parts as an array).
     642 *                          @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.
     647 */
     648function wp_parse_url( $url, $component = -1 ) {
     649    $parts = @parse_url( $url, $component );
     650
     651    if ( version_compare( PHP_VERSION, '5.4.7', '>=' ) ) {
     652        return $parts;
     653    }
     654
     655    if ( false === $parts ) {
     656        // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path.
    645657        if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) {
    646             // Since we know it's a relative path, prefix with a scheme/host placeholder and try again
    647             if ( ! $parts = @parse_url( 'placeholder://placeholder' . $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 ) ) {
    648663                return $parts;
    649664            }
    650             // Remove the placeholder values
    651             unset( $parts['scheme'], $parts['host'] );
     665            // Remove the placeholder values.
     666            if ( -1 === $component ) {
     667                unset( $parts['scheme'], $parts['host'] );
     668            }
    652669        } else {
    653670            return $parts;
     
    655672    }
    656673
    657     // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field
    658     if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) {
    659         $path_parts = explode( '/', substr( $parts['path'], 2 ), 2 );
    660         $parts['host'] = $path_parts[0];
    661         if ( isset( $path_parts[1] ) ) {
    662             $parts['path'] = '/' . $path_parts[1];
    663         } else {
    664             unset( $parts['path'] );
     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            }
    665698        }
    666699    }
  • trunk/tests/phpunit/tests/http/http.php

    r38456 r38694  
    77class Tests_HTTP_HTTP extends WP_UnitTestCase {
    88
     9    const FULL_TEST_URL = 'http://username:password@host.name:9090/path?arg1=value1&arg2=value2#anchor';
     10
    911    /**
    1012     * @dataProvider make_absolute_url_testcases
     
    7981        // 0: The URL, 1: The expected resulting structure
    8082        return array(
     83            array( self::FULL_TEST_URL, array(
     84                'scheme'   => 'http',
     85                'host'     => 'host.name',
     86                'port'     => 9090,
     87                'user'     => 'username',
     88                'pass'     => 'password',
     89                'path'     => '/path',
     90                'query'    => 'arg1=value1&arg2=value2',
     91                'fragment' => 'anchor',
     92            ) ),
    8193            array( 'http://example.com/', array( 'scheme' => 'http', 'host' => 'example.com', 'path' => '/' ) ),
    8294
     
    8698            array( 'http://example.com//path/', array( 'scheme' => 'http', 'host' => 'example.com', 'path' => '//path/' ) ),
    8799
    88             // < PHP 5.4.7: Scheme seperator in the URL
     100            // < PHP 5.4.7: Scheme separator in the URL.
    89101            array( 'http://example.com/http://example.net/', array( 'scheme' => 'http', 'host' => 'example.com', 'path' => '/http://example.net/' ) ),
    90102            array( '/path/http://example.net/', array( 'path' => '/path/http://example.net/' ) ),
     
    102114          - ://example.com - assumed path in PHP >= 5.4.7, fails in <5.4.7
    103115        */
     116    }
     117
     118    /**
     119     * @ticket 36356
     120     */
     121    function test_wp_parse_url_with_default_component() {
     122        $actual = wp_parse_url( self::FULL_TEST_URL, -1 );
     123        $this->assertEquals( array(
     124            'scheme'   => 'http',
     125            'host'     => 'host.name',
     126            'port'     => 9090,
     127            'user'     => 'username',
     128            'pass'     => 'password',
     129            'path'     => '/path',
     130            'query'    => 'arg1=value1&arg2=value2',
     131            'fragment' => 'anchor',
     132        ), $actual );
     133    }
     134
     135    /**
     136     * @ticket 36356
     137     *
     138     * @dataProvider parse_url_component_testcases
     139     */
     140    function test_wp_parse_url_with_component( $url, $component, $expected ) {
     141        $actual = wp_parse_url( $url, $component );
     142        $this->assertSame( $expected, $actual );
     143    }
     144
     145    function parse_url_component_testcases() {
     146        // 0: The URL, 1: The requested component, 2: The expected resulting structure.
     147        return array(
     148            array( self::FULL_TEST_URL, PHP_URL_SCHEME, 'http' ),
     149            array( self::FULL_TEST_URL, PHP_URL_USER, 'username' ),
     150            array( self::FULL_TEST_URL, PHP_URL_PASS, 'password' ),
     151            array( self::FULL_TEST_URL, PHP_URL_HOST, 'host.name' ),
     152            array( self::FULL_TEST_URL, PHP_URL_PORT, 9090 ),
     153            array( self::FULL_TEST_URL, PHP_URL_PATH, '/path' ),
     154            array( self::FULL_TEST_URL, PHP_URL_QUERY, 'arg1=value1&arg2=value2' ),
     155            array( self::FULL_TEST_URL, PHP_URL_FRAGMENT, 'anchor' ),
     156
     157            // < PHP 5.4.7: Schemeless URL.
     158            array( '//example.com/path/', PHP_URL_HOST, 'example.com' ),
     159            array( '//example.com/path/', PHP_URL_PATH, '/path/' ),
     160            array( '//example.com/', PHP_URL_HOST, 'example.com' ),
     161            array( '//example.com/', PHP_URL_PATH, '/' ),
     162            array( 'http://example.com//path/', PHP_URL_HOST, 'example.com' ),
     163            array( 'http://example.com//path/', PHP_URL_PATH, '//path/' ),
     164
     165            // < PHP 5.4.7: Scheme separator in the URL.
     166            array( 'http://example.com/http://example.net/', PHP_URL_HOST, 'example.com' ),
     167            array( 'http://example.com/http://example.net/', PHP_URL_PATH, '/http://example.net/' ),
     168            array( '/path/http://example.net/', PHP_URL_HOST, null ),
     169            array( '/path/http://example.net/', PHP_URL_PATH, '/path/http://example.net/' ),
     170
     171            // < PHP 5.4.7: IPv6 literals in schemeless URLs are handled incorrectly.
     172            array( '//[::FFFF::127.0.0.1]/', PHP_URL_HOST, '[::FFFF::127.0.0.1]' ),
     173            array( '//[::FFFF::127.0.0.1]/', PHP_URL_PATH, '/' ),
     174
     175            // PHP's parse_url() calls this an invalid URL, we handle it as a path.
     176            array( '/://example.com/', PHP_URL_PATH, '/://example.com/' ),
     177
     178        );
    104179    }
    105180
Note: See TracChangeset for help on using the changeset viewer.