home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / http.php < prev    next >
Encoding:
PHP Script  |  2017-09-22  |  21.7 KB  |  748 lines

  1. <?php
  2. /**
  3.  * Core HTTP Request API
  4.  *
  5.  * Standardizes the HTTP requests for WordPress. Handles cookies, gzip encoding and decoding, chunk
  6.  * decoding, if HTTP 1.1 and various other difficult HTTP protocol implementations.
  7.  *
  8.  * @package WordPress
  9.  * @subpackage HTTP
  10.  */
  11.  
  12. /**
  13.  * Returns the initialized WP_Http Object
  14.  *
  15.  * @since 2.7.0
  16.  * @access private
  17.  *
  18.  * @staticvar WP_Http $http
  19.  *
  20.  * @return WP_Http HTTP Transport object.
  21.  */
  22. function _wp_http_get_object() {
  23.     static $http = null;
  24.  
  25.     if ( is_null( $http ) ) {
  26.         $http = new WP_Http();
  27.     }
  28.     return $http;
  29. }
  30.  
  31. /**
  32.  * Retrieve the raw response from a safe HTTP request.
  33.  *
  34.  * This function is ideal when the HTTP request is being made to an arbitrary
  35.  * URL. The URL is validated to avoid redirection and request forgery attacks.
  36.  *
  37.  * @since 3.6.0
  38.  *
  39.  * @see wp_remote_request() For more information on the response array format.
  40.  * @see WP_Http::request() For default arguments information.
  41.  *
  42.  * @param string $url  Site URL to retrieve.
  43.  * @param array  $args Optional. Request arguments. Default empty array.
  44.  * @return WP_Error|array The response or WP_Error on failure.
  45.  */
  46. function wp_safe_remote_request( $url, $args = array() ) {
  47.     $args['reject_unsafe_urls'] = true;
  48.     $http = _wp_http_get_object();
  49.     return $http->request( $url, $args );
  50. }
  51.  
  52. /**
  53.  * Retrieve the raw response from a safe HTTP request using the GET method.
  54.  *
  55.  * This function is ideal when the HTTP request is being made to an arbitrary
  56.  * URL. The URL is validated to avoid redirection and request forgery attacks.
  57.  *
  58.  * @since 3.6.0
  59.  *
  60.  * @see wp_remote_request() For more information on the response array format.
  61.  * @see WP_Http::request() For default arguments information.
  62.  *
  63.  * @param string $url  Site URL to retrieve.
  64.  * @param array  $args Optional. Request arguments. Default empty array.
  65.  * @return WP_Error|array The response or WP_Error on failure.
  66.  */
  67. function wp_safe_remote_get( $url, $args = array() ) {
  68.     $args['reject_unsafe_urls'] = true;
  69.     $http = _wp_http_get_object();
  70.     return $http->get( $url, $args );
  71. }
  72.  
  73. /**
  74.  * Retrieve the raw response from a safe HTTP request using the POST method.
  75.  *
  76.  * This function is ideal when the HTTP request is being made to an arbitrary
  77.  * URL. The URL is validated to avoid redirection and request forgery attacks.
  78.  *
  79.  * @since 3.6.0
  80.  *
  81.  * @see wp_remote_request() For more information on the response array format.
  82.  * @see WP_Http::request() For default arguments information.
  83.  *
  84.  * @param string $url  Site URL to retrieve.
  85.  * @param array  $args Optional. Request arguments. Default empty array.
  86.  * @return WP_Error|array The response or WP_Error on failure.
  87.  */
  88. function wp_safe_remote_post( $url, $args = array() ) {
  89.     $args['reject_unsafe_urls'] = true;
  90.     $http = _wp_http_get_object();
  91.     return $http->post( $url, $args );
  92. }
  93.  
  94. /**
  95.  * Retrieve the raw response from a safe HTTP request using the HEAD method.
  96.  *
  97.  * This function is ideal when the HTTP request is being made to an arbitrary
  98.  * URL. The URL is validated to avoid redirection and request forgery attacks.
  99.  *
  100.  * @since 3.6.0
  101.  *
  102.  * @see wp_remote_request() For more information on the response array format.
  103.  * @see WP_Http::request() For default arguments information.
  104.  *
  105.  * @param string $url Site URL to retrieve.
  106.  * @param array $args Optional. Request arguments. Default empty array.
  107.  * @return WP_Error|array The response or WP_Error on failure.
  108.  */
  109. function wp_safe_remote_head( $url, $args = array() ) {
  110.     $args['reject_unsafe_urls'] = true;
  111.     $http = _wp_http_get_object();
  112.     return $http->head( $url, $args );
  113. }
  114.  
  115. /**
  116.  * Retrieve the raw response from the HTTP request.
  117.  *
  118.  * The array structure is a little complex:
  119.  *
  120.  *     $res = array(
  121.  *         'headers'  => array(),
  122.  *         'response' => array(
  123.  *             'code'    => int,
  124.  *             'message' => string
  125.  *         )
  126.  *     );
  127.  *
  128.  * All of the headers in $res['headers'] are with the name as the key and the
  129.  * value as the value. So to get the User-Agent, you would do the following.
  130.  *
  131.  *     $user_agent = $res['headers']['user-agent'];
  132.  *
  133.  * The body is the raw response content and can be retrieved from $res['body'].
  134.  *
  135.  * This function is called first to make the request and there are other API
  136.  * functions to abstract out the above convoluted setup.
  137.  *
  138.  * Request method defaults for helper functions:
  139.  *  - Default 'GET'  for wp_remote_get()
  140.  *  - Default 'POST' for wp_remote_post()
  141.  *  - Default 'HEAD' for wp_remote_head()
  142.  *
  143.  * @since 2.7.0
  144.  *
  145.  * @see WP_Http::request() For additional information on default arguments.
  146.  *
  147.  * @param string $url  Site URL to retrieve.
  148.  * @param array  $args Optional. Request arguments. Default empty array.
  149.  * @return WP_Error|array The response or WP_Error on failure.
  150.  */
  151. function wp_remote_request($url, $args = array()) {
  152.     $http = _wp_http_get_object();
  153.     return $http->request( $url, $args );
  154. }
  155.  
  156. /**
  157.  * Retrieve the raw response from the HTTP request using the GET method.
  158.  *
  159.  * @since 2.7.0
  160.  *
  161.  * @see wp_remote_request() For more information on the response array format.
  162.  * @see WP_Http::request() For default arguments information.
  163.  *
  164.  * @param string $url  Site URL to retrieve.
  165.  * @param array  $args Optional. Request arguments. Default empty array.
  166.  * @return WP_Error|array The response or WP_Error on failure.
  167.  */
  168. function wp_remote_get($url, $args = array()) {
  169.     $http = _wp_http_get_object();
  170.     return $http->get( $url, $args );
  171. }
  172.  
  173. /**
  174.  * Retrieve the raw response from the HTTP request using the POST method.
  175.  *
  176.  * @since 2.7.0
  177.  *
  178.  * @see wp_remote_request() For more information on the response array format.
  179.  * @see WP_Http::request() For default arguments information.
  180.  *
  181.  * @param string $url  Site URL to retrieve.
  182.  * @param array  $args Optional. Request arguments. Default empty array.
  183.  * @return WP_Error|array The response or WP_Error on failure.
  184.  */
  185. function wp_remote_post($url, $args = array()) {
  186.     $http = _wp_http_get_object();
  187.     return $http->post( $url, $args );
  188. }
  189.  
  190. /**
  191.  * Retrieve the raw response from the HTTP request using the HEAD method.
  192.  *
  193.  * @since 2.7.0
  194.  *
  195.  * @see wp_remote_request() For more information on the response array format.
  196.  * @see WP_Http::request() For default arguments information.
  197.  *
  198.  * @param string $url  Site URL to retrieve.
  199.  * @param array  $args Optional. Request arguments. Default empty array.
  200.  * @return WP_Error|array The response or WP_Error on failure.
  201.  */
  202. function wp_remote_head($url, $args = array()) {
  203.     $http = _wp_http_get_object();
  204.     return $http->head( $url, $args );
  205. }
  206.  
  207. /**
  208.  * Retrieve only the headers from the raw response.
  209.  *
  210.  * @since 2.7.0
  211.  * @since 4.6.0 Return value changed from an array to an Requests_Utility_CaseInsensitiveDictionary instance.
  212.  *
  213.  * @see \Requests_Utility_CaseInsensitiveDictionary
  214.  *
  215.  * @param array $response HTTP response.
  216.  * @return array|\Requests_Utility_CaseInsensitiveDictionary The headers of the response. Empty array if incorrect parameter given.
  217.  */
  218. function wp_remote_retrieve_headers( $response ) {
  219.     if ( is_wp_error( $response ) || ! isset( $response['headers'] ) ) {
  220.         return array();
  221.     }
  222.  
  223.     return $response['headers'];
  224. }
  225.  
  226. /**
  227.  * Retrieve a single header by name from the raw response.
  228.  *
  229.  * @since 2.7.0
  230.  *
  231.  * @param array  $response
  232.  * @param string $header Header name to retrieve value from.
  233.  * @return string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
  234.  */
  235. function wp_remote_retrieve_header( $response, $header ) {
  236.     if ( is_wp_error( $response ) || ! isset( $response['headers'] ) ) {
  237.         return '';
  238.     }
  239.  
  240.     if ( isset( $response['headers'][ $header ] ) ) {
  241.         return $response['headers'][$header];
  242.     }
  243.  
  244.     return '';
  245. }
  246.  
  247. /**
  248.  * Retrieve only the response code from the raw response.
  249.  *
  250.  * Will return an empty array if incorrect parameter value is given.
  251.  *
  252.  * @since 2.7.0
  253.  *
  254.  * @param array $response HTTP response.
  255.  * @return int|string The response code as an integer. Empty string on incorrect parameter given.
  256.  */
  257. function wp_remote_retrieve_response_code( $response ) {
  258.     if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
  259.         return '';
  260.  
  261.     return $response['response']['code'];
  262. }
  263.  
  264. /**
  265.  * Retrieve only the response message from the raw response.
  266.  *
  267.  * Will return an empty array if incorrect parameter value is given.
  268.  *
  269.  * @since 2.7.0
  270.  *
  271.  * @param array $response HTTP response.
  272.  * @return string The response message. Empty string on incorrect parameter given.
  273.  */
  274. function wp_remote_retrieve_response_message( $response ) {
  275.     if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
  276.         return '';
  277.  
  278.     return $response['response']['message'];
  279. }
  280.  
  281. /**
  282.  * Retrieve only the body from the raw response.
  283.  *
  284.  * @since 2.7.0
  285.  *
  286.  * @param array $response HTTP response.
  287.  * @return string The body of the response. Empty string if no body or incorrect parameter given.
  288.  */
  289. function wp_remote_retrieve_body( $response ) {
  290.     if ( is_wp_error($response) || ! isset($response['body']) )
  291.         return '';
  292.  
  293.     return $response['body'];
  294. }
  295.  
  296. /**
  297.  * Retrieve only the cookies from the raw response.
  298.  *
  299.  * @since 4.4.0
  300.  *
  301.  * @param array $response HTTP response.
  302.  * @return array An array of `WP_Http_Cookie` objects from the response. Empty array if there are none, or the response is a WP_Error.
  303.  */
  304. function wp_remote_retrieve_cookies( $response ) {
  305.     if ( is_wp_error( $response ) || empty( $response['cookies'] ) ) {
  306.         return array();
  307.     }
  308.  
  309.     return $response['cookies'];
  310. }
  311.  
  312. /**
  313.  * Retrieve a single cookie by name from the raw response.
  314.  *
  315.  * @since 4.4.0
  316.  *
  317.  * @param array  $response HTTP response.
  318.  * @param string $name     The name of the cookie to retrieve.
  319.  * @return WP_Http_Cookie|string The `WP_Http_Cookie` object. Empty string if the cookie isn't present in the response.
  320.  */
  321. function wp_remote_retrieve_cookie( $response, $name ) {
  322.     $cookies = wp_remote_retrieve_cookies( $response );
  323.  
  324.     if ( empty( $cookies ) ) {
  325.         return '';
  326.     }
  327.  
  328.     foreach ( $cookies as $cookie ) {
  329.         if ( $cookie->name === $name ) {
  330.             return $cookie;
  331.         }
  332.     }
  333.  
  334.     return '';
  335. }
  336.  
  337. /**
  338.  * Retrieve a single cookie's value by name from the raw response.
  339.  *
  340.  * @since 4.4.0
  341.  *
  342.  * @param array  $response HTTP response.
  343.  * @param string $name     The name of the cookie to retrieve.
  344.  * @return string The value of the cookie. Empty string if the cookie isn't present in the response.
  345.  */
  346. function wp_remote_retrieve_cookie_value( $response, $name ) {
  347.     $cookie = wp_remote_retrieve_cookie( $response, $name );
  348.  
  349.     if ( ! is_a( $cookie, 'WP_Http_Cookie' ) ) {
  350.         return '';
  351.     }
  352.  
  353.     return $cookie->value;
  354. }
  355.  
  356. /**
  357.  * Determines if there is an HTTP Transport that can process this request.
  358.  *
  359.  * @since 3.2.0
  360.  *
  361.  * @param array  $capabilities Array of capabilities to test or a wp_remote_request() $args array.
  362.  * @param string $url          Optional. If given, will check if the URL requires SSL and adds
  363.  *                             that requirement to the capabilities array.
  364.  *
  365.  * @return bool
  366.  */
  367. function wp_http_supports( $capabilities = array(), $url = null ) {
  368.     $http = _wp_http_get_object();
  369.  
  370.     $capabilities = wp_parse_args( $capabilities );
  371.  
  372.     $count = count( $capabilities );
  373.  
  374.     // If we have a numeric $capabilities array, spoof a wp_remote_request() associative $args array
  375.     if ( $count && count( array_filter( array_keys( $capabilities ), 'is_numeric' ) ) == $count ) {
  376.         $capabilities = array_combine( array_values( $capabilities ), array_fill( 0, $count, true ) );
  377.     }
  378.  
  379.     if ( $url && !isset( $capabilities['ssl'] ) ) {
  380.         $scheme = parse_url( $url, PHP_URL_SCHEME );
  381.         if ( 'https' == $scheme || 'ssl' == $scheme ) {
  382.             $capabilities['ssl'] = true;
  383.         }
  384.     }
  385.  
  386.     return (bool) $http->_get_first_available_transport( $capabilities );
  387. }
  388.  
  389. /**
  390.  * Get the HTTP Origin of the current request.
  391.  *
  392.  * @since 3.4.0
  393.  *
  394.  * @return string URL of the origin. Empty string if no origin.
  395.  */
  396. function get_http_origin() {
  397.     $origin = '';
  398.     if ( ! empty ( $_SERVER[ 'HTTP_ORIGIN' ] ) )
  399.         $origin = $_SERVER[ 'HTTP_ORIGIN' ];
  400.  
  401.     /**
  402.      * Change the origin of an HTTP request.
  403.      *
  404.      * @since 3.4.0
  405.      *
  406.      * @param string $origin The original origin for the request.
  407.      */
  408.     return apply_filters( 'http_origin', $origin );
  409. }
  410.  
  411. /**
  412.  * Retrieve list of allowed HTTP origins.
  413.  *
  414.  * @since 3.4.0
  415.  *
  416.  * @return array Array of origin URLs.
  417.  */
  418. function get_allowed_http_origins() {
  419.     $admin_origin = parse_url( admin_url() );
  420.     $home_origin = parse_url( home_url() );
  421.  
  422.     // @todo preserve port?
  423.     $allowed_origins = array_unique( array(
  424.         'http://' . $admin_origin[ 'host' ],
  425.         'https://' . $admin_origin[ 'host' ],
  426.         'http://' . $home_origin[ 'host' ],
  427.         'https://' . $home_origin[ 'host' ],
  428.     ) );
  429.  
  430.     /**
  431.      * Change the origin types allowed for HTTP requests.
  432.      *
  433.      * @since 3.4.0
  434.      *
  435.      * @param array $allowed_origins {
  436.      *     Default allowed HTTP origins.
  437.      *     @type string Non-secure URL for admin origin.
  438.      *     @type string Secure URL for admin origin.
  439.      *     @type string Non-secure URL for home origin.
  440.      *     @type string Secure URL for home origin.
  441.      * }
  442.      */
  443.     return apply_filters( 'allowed_http_origins' , $allowed_origins );
  444. }
  445.  
  446. /**
  447.  * Determines if the HTTP origin is an authorized one.
  448.  *
  449.  * @since 3.4.0
  450.  *
  451.  * @param null|string $origin Origin URL. If not provided, the value of get_http_origin() is used.
  452.  * @return string Origin URL if allowed, empty string if not.
  453.  */
  454. function is_allowed_http_origin( $origin = null ) {
  455.     $origin_arg = $origin;
  456.  
  457.     if ( null === $origin )
  458.         $origin = get_http_origin();
  459.  
  460.     if ( $origin && ! in_array( $origin, get_allowed_http_origins() ) )
  461.         $origin = '';
  462.  
  463.     /**
  464.      * Change the allowed HTTP origin result.
  465.      *
  466.      * @since 3.4.0
  467.      *
  468.      * @param string $origin     Origin URL if allowed, empty string if not.
  469.      * @param string $origin_arg Original origin string passed into is_allowed_http_origin function.
  470.      */
  471.     return apply_filters( 'allowed_http_origin', $origin, $origin_arg );
  472. }
  473.  
  474. /**
  475.  * Send Access-Control-Allow-Origin and related headers if the current request
  476.  * is from an allowed origin.
  477.  *
  478.  * If the request is an OPTIONS request, the script exits with either access
  479.  * control headers sent, or a 403 response if the origin is not allowed. For
  480.  * other request methods, you will receive a return value.
  481.  *
  482.  * @since 3.4.0
  483.  *
  484.  * @return string|false Returns the origin URL if headers are sent. Returns false
  485.  *                      if headers are not sent.
  486.  */
  487. function send_origin_headers() {
  488.     $origin = get_http_origin();
  489.  
  490.     if ( is_allowed_http_origin( $origin ) ) {
  491.         @header( 'Access-Control-Allow-Origin: ' .  $origin );
  492.         @header( 'Access-Control-Allow-Credentials: true' );
  493.         if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] )
  494.             exit;
  495.         return $origin;
  496.     }
  497.  
  498.     if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] ) {
  499.         status_header( 403 );
  500.         exit;
  501.     }
  502.  
  503.     return false;
  504. }
  505.  
  506. /**
  507.  * Validate a URL for safe use in the HTTP API.
  508.  *
  509.  * @since 3.5.2
  510.  *
  511.  * @param string $url
  512.  * @return false|string URL or false on failure.
  513.  */
  514. function wp_http_validate_url( $url ) {
  515.     $original_url = $url;
  516.     $url = wp_kses_bad_protocol( $url, array( 'http', 'https' ) );
  517.     if ( ! $url || strtolower( $url ) !== strtolower( $original_url ) )
  518.         return false;
  519.  
  520.     $parsed_url = @parse_url( $url );
  521.     if ( ! $parsed_url || empty( $parsed_url['host'] ) )
  522.         return false;
  523.  
  524.     if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) )
  525.         return false;
  526.  
  527.     if ( false !== strpbrk( $parsed_url['host'], ':#?[]' ) )
  528.         return false;
  529.  
  530.     $parsed_home = @parse_url( get_option( 'home' ) );
  531.  
  532.     if ( isset( $parsed_home['host'] ) ) {
  533.         $same_host = ( strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] ) || 'localhost' === strtolower( $parsed_url['host'] ) );
  534.     } else {
  535.         $same_host = false;
  536.     }
  537.  
  538.     if ( ! $same_host ) {
  539.         $host = trim( $parsed_url['host'], '.' );
  540.         if ( preg_match( '#^(([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)\.){3}([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)$#', $host ) ) {
  541.             $ip = $host;
  542.         } else {
  543.             $ip = gethostbyname( $host );
  544.             if ( $ip === $host ) // Error condition for gethostbyname()
  545.                 $ip = false;
  546.         }
  547.         if ( $ip ) {
  548.             $parts = array_map( 'intval', explode( '.', $ip ) );
  549.             if ( 127 === $parts[0] || 10 === $parts[0] || 0 === $parts[0]
  550.                 || ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] )
  551.                 || ( 192 === $parts[0] && 168 === $parts[1] )
  552.             ) {
  553.                 // If host appears local, reject unless specifically allowed.
  554.                 /**
  555.                  * Check if HTTP request is external or not.
  556.                  *
  557.                  * Allows to change and allow external requests for the HTTP request.
  558.                  *
  559.                  * @since 3.6.0
  560.                  *
  561.                  * @param bool   false Whether HTTP request is external or not.
  562.                  * @param string $host IP of the requested host.
  563.                  * @param string $url  URL of the requested host.
  564.                  */
  565.                 if ( ! apply_filters( 'http_request_host_is_external', false, $host, $url ) )
  566.                     return false;
  567.             }
  568.         }
  569.     }
  570.  
  571.     if ( empty( $parsed_url['port'] ) )
  572.         return $url;
  573.  
  574.     $port = $parsed_url['port'];
  575.     if ( 80 === $port || 443 === $port || 8080 === $port )
  576.         return $url;
  577.  
  578.     if ( $parsed_home && $same_host && isset( $parsed_home['port'] ) && $parsed_home['port'] === $port )
  579.         return $url;
  580.  
  581.     return false;
  582. }
  583.  
  584. /**
  585.  * Whitelists allowed redirect hosts for safe HTTP requests as well.
  586.  *
  587.  * Attached to the {@see 'http_request_host_is_external'} filter.
  588.  *
  589.  * @since 3.6.0
  590.  *
  591.  * @param bool   $is_external
  592.  * @param string $host
  593.  * @return bool
  594.  */
  595. function allowed_http_request_hosts( $is_external, $host ) {
  596.     if ( ! $is_external && wp_validate_redirect( 'http://' . $host ) )
  597.         $is_external = true;
  598.     return $is_external;
  599. }
  600.  
  601. /**
  602.  * Whitelists any domain in a multisite installation for safe HTTP requests.
  603.  *
  604.  * Attached to the {@see 'http_request_host_is_external'} filter.
  605.  *
  606.  * @since 3.6.0
  607.  *
  608.  * @global wpdb $wpdb WordPress database abstraction object.
  609.  * @staticvar array $queried
  610.  *
  611.  * @param bool   $is_external
  612.  * @param string $host
  613.  * @return bool
  614.  */
  615. function ms_allowed_http_request_hosts( $is_external, $host ) {
  616.     global $wpdb;
  617.     static $queried = array();
  618.     if ( $is_external )
  619.         return $is_external;
  620.     if ( $host === get_network()->domain )
  621.         return true;
  622.     if ( isset( $queried[ $host ] ) )
  623.         return $queried[ $host ];
  624.     $queried[ $host ] = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM $wpdb->blogs WHERE domain = %s LIMIT 1", $host ) );
  625.     return $queried[ $host ];
  626. }
  627.  
  628. /**
  629.  * A wrapper for PHP's parse_url() function that handles consistency in the return
  630.  * values across PHP versions.
  631.  *
  632.  * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including
  633.  * schemeless and relative url's with :// in the path. This function works around
  634.  * those limitations providing a standard output on PHP 5.2~5.4+.
  635.  *
  636.  * Secondly, across various PHP versions, schemeless URLs starting containing a ":"
  637.  * in the query are being handled inconsistently. This function works around those
  638.  * differences as well.
  639.  *
  640.  * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated
  641.  * when URL parsing failed.
  642.  *
  643.  * @since 4.4.0
  644.  * @since 4.7.0 The $component parameter was added for parity with PHP's parse_url().
  645.  *
  646.  * @link https://secure.php.net/manual/en/function.parse-url.php
  647.  *
  648.  * @param string $url       The URL to parse.
  649.  * @param int    $component The specific component to retrieve. Use one of the PHP
  650.  *                          predefined constants to specify which one.
  651.  *                          Defaults to -1 (= return all parts as an array).
  652.  * @return mixed False on parse failure; Array of URL components on success;
  653.  *               When a specific component has been requested: null if the component
  654.  *               doesn't exist in the given URL; a string or - in the case of
  655.  *               PHP_URL_PORT - integer when it does. See parse_url()'s return values.
  656.  */
  657. function wp_parse_url( $url, $component = -1 ) {
  658.     $to_unset = array();
  659.     $url = strval( $url );
  660.  
  661.     if ( '//' === substr( $url, 0, 2 ) ) {
  662.         $to_unset[] = 'scheme';
  663.         $url = 'placeholder:' . $url;
  664.     } elseif ( '/' === substr( $url, 0, 1 ) ) {
  665.         $to_unset[] = 'scheme';
  666.         $to_unset[] = 'host';
  667.         $url = 'placeholder://placeholder' . $url;
  668.     }
  669.  
  670.     $parts = @parse_url( $url );
  671.  
  672.     if ( false === $parts ) {
  673.         // Parsing failure.
  674.         return $parts;
  675.     }
  676.  
  677.     // Remove the placeholder values.
  678.     foreach ( $to_unset as $key ) {
  679.         unset( $parts[ $key ] );
  680.     }
  681.  
  682.     return _get_component_from_parsed_url_array( $parts, $component );
  683. }
  684.  
  685. /**
  686.  * Retrieve a specific component from a parsed URL array.
  687.  *
  688.  * @internal
  689.  *
  690.  * @since 4.7.0
  691.  * @access private
  692.  *
  693.  * @link https://secure.php.net/manual/en/function.parse-url.php
  694.  *
  695.  * @param array|false $url_parts The parsed URL. Can be false if the URL failed to parse.
  696.  * @param int    $component The specific component to retrieve. Use one of the PHP
  697.  *                          predefined constants to specify which one.
  698.  *                          Defaults to -1 (= return all parts as an array).
  699.  * @return mixed False on parse failure; Array of URL components on success;
  700.  *               When a specific component has been requested: null if the component
  701.  *               doesn't exist in the given URL; a string or - in the case of
  702.  *               PHP_URL_PORT - integer when it does. See parse_url()'s return values.
  703.  */
  704. function _get_component_from_parsed_url_array( $url_parts, $component = -1 ) {
  705.     if ( -1 === $component ) {
  706.         return $url_parts;
  707.     }
  708.  
  709.     $key = _wp_translate_php_url_constant_to_key( $component );
  710.     if ( false !== $key && is_array( $url_parts ) && isset( $url_parts[ $key ] ) ) {
  711.         return $url_parts[ $key ];
  712.     } else {
  713.         return null;
  714.     }
  715. }
  716.  
  717. /**
  718.  * Translate a PHP_URL_* constant to the named array keys PHP uses.
  719.  *
  720.  * @internal
  721.  *
  722.  * @since 4.7.0
  723.  * @access private
  724.  *
  725.  * @link https://secure.php.net/manual/en/url.constants.php
  726.  *
  727.  * @param int $constant PHP_URL_* constant.
  728.  * @return string|bool The named key or false.
  729.  */
  730. function _wp_translate_php_url_constant_to_key( $constant ) {
  731.     $translation = array(
  732.         PHP_URL_SCHEME   => 'scheme',
  733.         PHP_URL_HOST     => 'host',
  734.         PHP_URL_PORT     => 'port',
  735.         PHP_URL_USER     => 'user',
  736.         PHP_URL_PASS     => 'pass',
  737.         PHP_URL_PATH     => 'path',
  738.         PHP_URL_QUERY    => 'query',
  739.         PHP_URL_FRAGMENT => 'fragment',
  740.     );
  741.  
  742.     if ( isset( $translation[ $constant ] ) ) {
  743.         return $translation[ $constant ];
  744.     } else {
  745.         return false;
  746.     }
  747. }
  748.