ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

http.php (22385B)


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