Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/http.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
 *
212
 * @param array $response HTTP response.
213
 * @return array The headers of the response. Empty array if incorrect parameter given.
214
 */
215
function wp_remote_retrieve_headers( $response ) {
216
	if ( is_wp_error( $response ) || ! isset( $response['headers'] ) ) {
217
		return array();
218
	}
219
220
	return $response['headers'];
221
}
222
223
/**
224
 * Retrieve a single header by name from the raw response.
225
 *
226
 * @since 2.7.0
227
 *
228
 * @param array  $response
229
 * @param string $header Header name to retrieve value from.
230
 * @return string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
231
 */
232
function wp_remote_retrieve_header( $response, $header ) {
233
	if ( is_wp_error( $response ) || ! isset( $response['headers'] ) ) {
234
		return '';
235
	}
236
237
	if ( isset( $response['headers'][ $header ] ) ) {
238
		return $response['headers'][$header];
239
	}
240
241
	return '';
242
}
243
244
/**
245
 * Retrieve only the response code from the raw response.
246
 *
247
 * Will return an empty array if incorrect parameter value is given.
248
 *
249
 * @since 2.7.0
250
 *
251
 * @param array $response HTTP response.
252
 * @return int|string The response code as an integer. Empty string on incorrect parameter given.
253
 */
254
function wp_remote_retrieve_response_code( $response ) {
255 View Code Duplication
	if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
256
		return '';
257
258
	return $response['response']['code'];
259
}
260
261
/**
262
 * Retrieve only the response message from the raw response.
263
 *
264
 * Will return an empty array if incorrect parameter value is given.
265
 *
266
 * @since 2.7.0
267
 *
268
 * @param array $response HTTP response.
269
 * @return string The response message. Empty string on incorrect parameter given.
270
 */
271
function wp_remote_retrieve_response_message( $response ) {
272 View Code Duplication
	if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
273
		return '';
274
275
	return $response['response']['message'];
276
}
277
278
/**
279
 * Retrieve only the body from the raw response.
280
 *
281
 * @since 2.7.0
282
 *
283
 * @param array $response HTTP response.
284
 * @return string The body of the response. Empty string if no body or incorrect parameter given.
285
 */
286
function wp_remote_retrieve_body( $response ) {
287
	if ( is_wp_error($response) || ! isset($response['body']) )
288
		return '';
289
290
	return $response['body'];
291
}
292
293
/**
294
 * Retrieve only the cookies from the raw response.
295
 *
296
 * @since 4.4.0
297
 *
298
 * @param array $response HTTP response.
299
 * @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.
300
 */
301
function wp_remote_retrieve_cookies( $response ) {
302
	if ( is_wp_error( $response ) || empty( $response['cookies'] ) ) {
303
		return array();
304
	}
305
306
	return $response['cookies'];
307
}
308
309
/**
310
 * Retrieve a single cookie by name from the raw response.
311
 *
312
 * @since 4.4.0
313
 *
314
 * @param array  $response HTTP response.
315
 * @param string $name     The name of the cookie to retrieve.
316
 * @return WP_Http_Cookie|string The `WP_Http_Cookie` object. Empty string if the cookie isn't present in the response.
317
 */
318
function wp_remote_retrieve_cookie( $response, $name ) {
319
	$cookies = wp_remote_retrieve_cookies( $response );
320
321
	if ( empty( $cookies ) ) {
322
		return '';
323
	}
324
325
	foreach ( $cookies as $cookie ) {
326
		if ( $cookie->name === $name ) {
327
			return $cookie;
328
		}
329
	}
330
331
	return '';
332
}
333
334
/**
335
 * Retrieve a single cookie's value by name from the raw response.
336
 *
337
 * @since 4.4.0
338
 *
339
 * @param array  $response HTTP response.
340
 * @param string $name     The name of the cookie to retrieve.
341
 * @return string The value of the cookie. Empty string if the cookie isn't present in the response.
342
 */
343
function wp_remote_retrieve_cookie_value( $response, $name ) {
344
	$cookie = wp_remote_retrieve_cookie( $response, $name );
345
346
	if ( ! is_a( $cookie, 'WP_Http_Cookie' ) ) {
347
		return '';
348
	}
349
350
	return $cookie->value;
351
}
352
353
/**
354
 * Determines if there is an HTTP Transport that can process this request.
355
 *
356
 * @since 3.2.0
357
 *
358
 * @param array  $capabilities Array of capabilities to test or a wp_remote_request() $args array.
359
 * @param string $url          Optional. If given, will check if the URL requires SSL and adds
360
 *                             that requirement to the capabilities array.
361
 *
362
 * @return bool
363
 */
364
function wp_http_supports( $capabilities = array(), $url = null ) {
365
	$http = _wp_http_get_object();
366
367
	$capabilities = wp_parse_args( $capabilities );
368
369
	$count = count( $capabilities );
370
371
	// If we have a numeric $capabilities array, spoof a wp_remote_request() associative $args array
372
	if ( $count && count( array_filter( array_keys( $capabilities ), 'is_numeric' ) ) == $count ) {
373
		$capabilities = array_combine( array_values( $capabilities ), array_fill( 0, $count, true ) );
374
	}
375
376
	if ( $url && !isset( $capabilities['ssl'] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
377
		$scheme = parse_url( $url, PHP_URL_SCHEME );
378
		if ( 'https' == $scheme || 'ssl' == $scheme ) {
379
			$capabilities['ssl'] = true;
380
		}
381
	}
382
383
	return (bool) $http->_get_first_available_transport( $capabilities );
384
}
385
386
/**
387
 * Get the HTTP Origin of the current request.
388
 *
389
 * @since 3.4.0
390
 *
391
 * @return string URL of the origin. Empty string if no origin.
392
 */
393
function get_http_origin() {
394
	$origin = '';
395
	if ( ! empty ( $_SERVER[ 'HTTP_ORIGIN' ] ) )
396
		$origin = $_SERVER[ 'HTTP_ORIGIN' ];
397
398
	/**
399
	 * Change the origin of an HTTP request.
400
	 *
401
	 * @since 3.4.0
402
	 *
403
	 * @param string $origin The original origin for the request.
404
	 */
405
	return apply_filters( 'http_origin', $origin );
406
}
407
408
/**
409
 * Retrieve list of allowed HTTP origins.
410
 *
411
 * @since 3.4.0
412
 *
413
 * @return array Array of origin URLs.
414
 */
415
function get_allowed_http_origins() {
416
	$admin_origin = parse_url( admin_url() );
417
	$home_origin = parse_url( home_url() );
418
419
	// @todo preserve port?
420
	$allowed_origins = array_unique( 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
	 * Change the origin types allowed for HTTP requests.
429
	 *
430
	 * @since 3.4.0
431
	 *
432
	 * @param array $allowed_origins {
433
	 *     Default allowed HTTP origins.
434
	 *     @type string Non-secure URL for admin origin.
435
	 *     @type string Secure URL for admin origin.
436
	 *     @type string Non-secure URL for home origin.
437
	 *     @type string Secure URL for home origin.
438
	 * }
439
	 */
440
	return apply_filters( 'allowed_http_origins' , $allowed_origins );
441
}
442
443
/**
444
 * Determines if the HTTP origin is an authorized one.
445
 *
446
 * @since 3.4.0
447
 *
448
 * @param null|string $origin Origin URL. If not provided, the value of get_http_origin() is used.
449
 * @return string Origin URL if allowed, empty string if not.
450
 */
451
function is_allowed_http_origin( $origin = null ) {
452
	$origin_arg = $origin;
453
454
	if ( null === $origin )
455
		$origin = get_http_origin();
456
457
	if ( $origin && ! in_array( $origin, get_allowed_http_origins() ) )
458
		$origin = '';
459
460
	/**
461
	 * Change the allowed HTTP origin result.
462
	 *
463
	 * @since 3.4.0
464
	 *
465
	 * @param string $origin     Origin URL if allowed, empty string if not.
466
	 * @param string $origin_arg Original origin string passed into is_allowed_http_origin function.
467
	 */
468
	return apply_filters( 'allowed_http_origin', $origin, $origin_arg );
469
}
470
471
/**
472
 * Send Access-Control-Allow-Origin and related headers if the current request
473
 * is from an allowed origin.
474
 *
475
 * If the request is an OPTIONS request, the script exits with either access
476
 * control headers sent, or a 403 response if the origin is not allowed. For
477
 * other request methods, you will receive a return value.
478
 *
479
 * @since 3.4.0
480
 *
481
 * @return string|false Returns the origin URL if headers are sent. Returns false
482
 *                      if headers are not sent.
483
 */
484
function send_origin_headers() {
485
	$origin = get_http_origin();
486
487
	if ( is_allowed_http_origin( $origin ) ) {
488
		@header( 'Access-Control-Allow-Origin: ' .  $origin );
489
		@header( 'Access-Control-Allow-Credentials: true' );
490
		if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] )
491
			exit;
492
		return $origin;
493
	}
494
495
	if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] ) {
496
		status_header( 403 );
497
		exit;
498
	}
499
500
	return false;
501
}
502
503
/**
504
 * Validate a URL for safe use in the HTTP API.
505
 *
506
 * @since 3.5.2
507
 *
508
 * @param string $url
509
 * @return false|string URL or false on failure.
510
 */
511
function wp_http_validate_url( $url ) {
512
	$original_url = $url;
513
	$url = wp_kses_bad_protocol( $url, array( 'http', 'https' ) );
514
	if ( ! $url || strtolower( $url ) !== strtolower( $original_url ) )
515
		return false;
516
517
	$parsed_url = @parse_url( $url );
518
	if ( ! $parsed_url || empty( $parsed_url['host'] ) )
519
		return false;
520
521
	if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) )
522
		return false;
523
524
	if ( false !== strpbrk( $parsed_url['host'], ':#?[]' ) )
525
		return false;
526
527
	$parsed_home = @parse_url( get_option( 'home' ) );
528
529
	if ( isset( $parsed_home['host'] ) ) {
530
		$same_host = ( strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] ) || 'localhost' === strtolower( $parsed_url['host'] ) );
531
	} else {
532
		$same_host = false;
533
	}
534
535
	if ( ! $same_host ) {
536
		$host = trim( $parsed_url['host'], '.' );
537
		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 ) ) {
538
			$ip = $host;
539
		} else {
540
			$ip = gethostbyname( $host );
541
			if ( $ip === $host ) // Error condition for gethostbyname()
542
				$ip = false;
543
		}
544
		if ( $ip ) {
545
			$parts = array_map( 'intval', explode( '.', $ip ) );
546
			if ( 127 === $parts[0] || 10 === $parts[0] || 0 === $parts[0]
547
				|| ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] )
548
				|| ( 192 === $parts[0] && 168 === $parts[1] )
549
			) {
550
				// If host appears local, reject unless specifically allowed.
551
				/**
552
				 * Check if HTTP request is external or not.
553
				 *
554
				 * Allows to change and allow external requests for the HTTP request.
555
				 *
556
				 * @since 3.6.0
557
				 *
558
				 * @param bool   false Whether HTTP request is external or not.
559
				 * @param string $host IP of the requested host.
560
				 * @param string $url  URL of the requested host.
561
				 */
562
				if ( ! apply_filters( 'http_request_host_is_external', false, $host, $url ) )
563
					return false;
564
			}
565
		}
566
	}
567
568
	if ( empty( $parsed_url['port'] ) )
569
		return $url;
570
571
	$port = $parsed_url['port'];
572
	if ( 80 === $port || 443 === $port || 8080 === $port )
573
		return $url;
574
575
	if ( $parsed_home && $same_host && isset( $parsed_home['port'] ) && $parsed_home['port'] === $port )
576
		return $url;
577
578
	return false;
579
}
580
581
/**
582
 * Whitelists allowed redirect hosts for safe HTTP requests as well.
583
 *
584
 * Attached to the {@see 'http_request_host_is_external'} filter.
585
 *
586
 * @since 3.6.0
587
 *
588
 * @param bool   $is_external
589
 * @param string $host
590
 * @return bool
591
 */
592
function allowed_http_request_hosts( $is_external, $host ) {
593
	if ( ! $is_external && wp_validate_redirect( 'http://' . $host ) )
594
		$is_external = true;
595
	return $is_external;
596
}
597
598
/**
599
 * Whitelists any domain in a multisite installation for safe HTTP requests.
600
 *
601
 * Attached to the {@see 'http_request_host_is_external'} filter.
602
 *
603
 * @since 3.6.0
604
 *
605
 * @global wpdb $wpdb WordPress database abstraction object.
606
 * @staticvar array $queried
607
 *
608
 * @param bool   $is_external
609
 * @param string $host
610
 * @return bool
611
 */
612
function ms_allowed_http_request_hosts( $is_external, $host ) {
613
	global $wpdb;
614
	static $queried = array();
615
	if ( $is_external )
616
		return $is_external;
617
	if ( $host === get_current_site()->domain )
618
		return true;
619
	if ( isset( $queried[ $host ] ) )
620
		return $queried[ $host ];
621
	$queried[ $host ] = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM $wpdb->blogs WHERE domain = %s LIMIT 1", $host ) );
622
	return $queried[ $host ];
623
}
624
625
/**
626
 * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7
627
 *
628
 * 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+.
631
 *
632
 * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated
633
 * when URL parsing failed.
634
 *
635
 * @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
645
		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 ) ) {
648
				return $parts;
649
			}
650
			// Remove the placeholder values
651
			unset( $parts['scheme'], $parts['host'] );
652
		} else {
653
			return $parts;
654
		}
655
	}
656
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'] );
665
		}
666
	}
667
668
	return $parts;
669
}
670