WP_Http::make_absolute_url()   C
last analyzed

Complexity

Conditions 14
Paths 52

Size

Total Lines 60
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 30
c 1
b 0
f 0
nc 52
nop 2
dl 0
loc 60
rs 6.325

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * HTTP API: WP_Http class
4
 *
5
 * @package WordPress
6
 * @subpackage HTTP
7
 * @since 2.7.0
8
 */
9
10
if ( ! class_exists( 'Requests' ) ) {
11
	require( ABSPATH . WPINC . '/class-requests.php' );
12
13
	Requests::register_autoloader();
14
	Requests::set_certificate_path( ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
15
}
16
17
/**
18
 * Core class used for managing HTTP transports and making HTTP requests.
19
 *
20
 * This class is used to consistently make outgoing HTTP requests easy for developers
21
 * while still being compatible with the many PHP configurations under which
22
 * WordPress runs.
23
 *
24
 * Debugging includes several actions, which pass different variables for debugging the HTTP API.
25
 *
26
 * @since 2.7.0
27
 */
28
class WP_Http {
29
30
	// Aliases for HTTP response codes.
31
	const HTTP_CONTINUE                   = 100;
32
	const SWITCHING_PROTOCOLS             = 101;
33
	const PROCESSING                      = 102;
34
35
	const OK                              = 200;
36
	const CREATED                         = 201;
37
	const ACCEPTED                        = 202;
38
	const NON_AUTHORITATIVE_INFORMATION   = 203;
39
	const NO_CONTENT                      = 204;
40
	const RESET_CONTENT                   = 205;
41
	const PARTIAL_CONTENT                 = 206;
42
	const MULTI_STATUS                    = 207;
43
	const IM_USED                         = 226;
44
45
	const MULTIPLE_CHOICES                = 300;
46
	const MOVED_PERMANENTLY               = 301;
47
	const FOUND                           = 302;
48
	const SEE_OTHER                       = 303;
49
	const NOT_MODIFIED                    = 304;
50
	const USE_PROXY                       = 305;
51
	const RESERVED                        = 306;
52
	const TEMPORARY_REDIRECT              = 307;
53
	const PERMANENT_REDIRECT              = 308;
54
55
	const BAD_REQUEST                     = 400;
56
	const UNAUTHORIZED                    = 401;
57
	const PAYMENT_REQUIRED                = 402;
58
	const FORBIDDEN                       = 403;
59
	const NOT_FOUND                       = 404;
60
	const METHOD_NOT_ALLOWED              = 405;
61
	const NOT_ACCEPTABLE                  = 406;
62
	const PROXY_AUTHENTICATION_REQUIRED   = 407;
63
	const REQUEST_TIMEOUT                 = 408;
64
	const CONFLICT                        = 409;
65
	const GONE                            = 410;
66
	const LENGTH_REQUIRED                 = 411;
67
	const PRECONDITION_FAILED             = 412;
68
	const REQUEST_ENTITY_TOO_LARGE        = 413;
69
	const REQUEST_URI_TOO_LONG            = 414;
70
	const UNSUPPORTED_MEDIA_TYPE          = 415;
71
	const REQUESTED_RANGE_NOT_SATISFIABLE = 416;
72
	const EXPECTATION_FAILED              = 417;
73
	const IM_A_TEAPOT                     = 418;
74
	const MISDIRECTED_REQUEST             = 421;
75
	const UNPROCESSABLE_ENTITY            = 422;
76
	const LOCKED                          = 423;
77
	const FAILED_DEPENDENCY               = 424;
78
	const UPGRADE_REQUIRED                = 426;
79
	const PRECONDITION_REQUIRED           = 428;
80
	const TOO_MANY_REQUESTS               = 429;
81
	const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
82
	const UNAVAILABLE_FOR_LEGAL_REASONS   = 451;
83
84
	const INTERNAL_SERVER_ERROR           = 500;
85
	const NOT_IMPLEMENTED                 = 501;
86
	const BAD_GATEWAY                     = 502;
87
	const SERVICE_UNAVAILABLE             = 503;
88
	const GATEWAY_TIMEOUT                 = 504;
89
	const HTTP_VERSION_NOT_SUPPORTED      = 505;
90
	const VARIANT_ALSO_NEGOTIATES         = 506;
91
	const INSUFFICIENT_STORAGE            = 507;
92
	const NOT_EXTENDED                    = 510;
93
	const NETWORK_AUTHENTICATION_REQUIRED = 511;
94
95
	/**
96
	 * Send an HTTP request to a URI.
97
	 *
98
	 * Please note: The only URI that are supported in the HTTP Transport implementation
99
	 * are the HTTP and HTTPS protocols.
100
	 *
101
	 * @access public
102
	 * @since 2.7.0
103
	 *
104
	 * @global string $wp_version
105
	 *
106
	 * @param string       $url  The request URL.
107
	 * @param string|array $args {
108
	 *     Optional. Array or string of HTTP request arguments.
109
	 *
110
	 *     @type string       $method              Request method. Accepts 'GET', 'POST', 'HEAD', or 'PUT'.
111
	 *                                             Some transports technically allow others, but should not be
112
	 *                                             assumed. Default 'GET'.
113
	 *     @type int          $timeout             How long the connection should stay open in seconds. Default 5.
114
	 *     @type int          $redirection         Number of allowed redirects. Not supported by all transports
115
	 *                                             Default 5.
116
	 *     @type string       $httpversion         Version of the HTTP protocol to use. Accepts '1.0' and '1.1'.
117
	 *                                             Default '1.0'.
118
	 *     @type string       $user-agent          User-agent value sent.
119
	 *                                             Default WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ).
120
	 *     @type bool         $reject_unsafe_urls  Whether to pass URLs through wp_http_validate_url().
121
	 *                                             Default false.
122
	 *     @type bool         $blocking            Whether the calling code requires the result of the request.
123
	 *                                             If set to false, the request will be sent to the remote server,
124
	 *                                             and processing returned to the calling code immediately, the caller
125
	 *                                             will know if the request succeeded or failed, but will not receive
126
	 *                                             any response from the remote server. Default true.
127
	 *     @type string|array $headers             Array or string of headers to send with the request.
128
	 *                                             Default empty array.
129
	 *     @type array        $cookies             List of cookies to send with the request. Default empty array.
130
	 *     @type string|array $body                Body to send with the request. Default null.
131
	 *     @type bool         $compress            Whether to compress the $body when sending the request.
132
	 *                                             Default false.
133
	 *     @type bool         $decompress          Whether to decompress a compressed response. If set to false and
134
	 *                                             compressed content is returned in the response anyway, it will
135
	 *                                             need to be separately decompressed. Default true.
136
	 *     @type bool         $sslverify           Whether to verify SSL for the request. Default true.
137
	 *     @type string       sslcertificates      Absolute path to an SSL certificate .crt file.
138
	 *                                             Default ABSPATH . WPINC . '/certificates/ca-bundle.crt'.
139
	 *     @type bool         $stream              Whether to stream to a file. If set to true and no filename was
140
	 *                                             given, it will be droped it in the WP temp dir and its name will
141
	 *                                             be set using the basename of the URL. Default false.
142
	 *     @type string       $filename            Filename of the file to write to when streaming. $stream must be
143
	 *                                             set to true. Default null.
144
	 *     @type int          $limit_response_size Size in bytes to limit the response to. Default null.
145
	 *
146
	 * }
147
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
148
	 *                        A WP_Error instance upon error.
149
	 */
150
	public function request( $url, $args = array() ) {
151
		global $wp_version;
152
153
		$defaults = array(
154
			'method' => 'GET',
155
			/**
156
			 * Filters the timeout value for an HTTP request.
157
			 *
158
			 * @since 2.7.0
159
			 *
160
			 * @param int $timeout_value Time in seconds until a request times out.
161
			 *                           Default 5.
162
			 */
163
			'timeout' => apply_filters( 'http_request_timeout', 5 ),
164
			/**
165
			 * Filters the number of redirects allowed during an HTTP request.
166
			 *
167
			 * @since 2.7.0
168
			 *
169
			 * @param int $redirect_count Number of redirects allowed. Default 5.
170
			 */
171
			'redirection' => apply_filters( 'http_request_redirection_count', 5 ),
172
			/**
173
			 * Filters the version of the HTTP protocol used in a request.
174
			 *
175
			 * @since 2.7.0
176
			 *
177
			 * @param string $version Version of HTTP used. Accepts '1.0' and '1.1'.
178
			 *                        Default '1.0'.
179
			 */
180
			'httpversion' => apply_filters( 'http_request_version', '1.0' ),
181
			/**
182
			 * Filters the user agent value sent with an HTTP request.
183
			 *
184
			 * @since 2.7.0
185
			 *
186
			 * @param string $user_agent WordPress user agent string.
187
			 */
188
			'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) ),
189
			/**
190
			 * Filters whether to pass URLs through wp_http_validate_url() in an HTTP request.
191
			 *
192
			 * @since 3.6.0
193
			 *
194
			 * @param bool $pass_url Whether to pass URLs through wp_http_validate_url().
195
			 *                       Default false.
196
			 */
197
			'reject_unsafe_urls' => apply_filters( 'http_request_reject_unsafe_urls', false ),
198
			'blocking' => true,
199
			'headers' => array(),
200
			'cookies' => array(),
201
			'body' => null,
202
			'compress' => false,
203
			'decompress' => true,
204
			'sslverify' => true,
205
			'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
206
			'stream' => false,
207
			'filename' => null,
208
			'limit_response_size' => null,
209
		);
210
211
		// Pre-parse for the HEAD checks.
212
		$args = wp_parse_args( $args );
213
214
		// By default, Head requests do not cause redirections.
215
		if ( isset($args['method']) && 'HEAD' == $args['method'] )
216
			$defaults['redirection'] = 0;
217
218
		$r = wp_parse_args( $args, $defaults );
219
		/**
220
		 * Filters the arguments used in an HTTP request.
221
		 *
222
		 * @since 2.7.0
223
		 *
224
		 * @param array  $r   An array of HTTP request arguments.
225
		 * @param string $url The request URL.
226
		 */
227
		$r = apply_filters( 'http_request_args', $r, $url );
228
229
		// The transports decrement this, store a copy of the original value for loop purposes.
230
		if ( ! isset( $r['_redirection'] ) )
231
			$r['_redirection'] = $r['redirection'];
232
233
		/**
234
		 * Filters whether to preempt an HTTP request's return value.
235
		 *
236
		 * Returning a non-false value from the filter will short-circuit the HTTP request and return
237
		 * early with that value. A filter should return either:
238
		 *
239
		 *  - An array containing 'headers', 'body', 'response', 'cookies', and 'filename' elements
240
		 *  - A WP_Error instance
241
		 *  - boolean false (to avoid short-circuiting the response)
242
		 *
243
		 * Returning any other value may result in unexpected behaviour.
244
		 *
245
		 * @since 2.9.0
246
		 *
247
		 * @param false|array|WP_Error $preempt Whether to preempt an HTTP request's return value. Default false.
248
		 * @param array               $r        HTTP request arguments.
249
		 * @param string              $url      The request URL.
250
		 */
251
		$pre = apply_filters( 'pre_http_request', false, $r, $url );
252
253
		if ( false !== $pre )
254
			return $pre;
255
256
		if ( function_exists( 'wp_kses_bad_protocol' ) ) {
257
			if ( $r['reject_unsafe_urls'] ) {
258
				$url = wp_http_validate_url( $url );
259
			}
260
			if ( $url ) {
261
				$url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) );
262
			}
263
		}
264
265
		$arrURL = @parse_url( $url );
266
267
		if ( empty( $url ) || empty( $arrURL['scheme'] ) ) {
268
			return new WP_Error('http_request_failed', __('A valid URL was not provided.'));
269
		}
270
271
		if ( $this->block_request( $url ) ) {
272
			return new WP_Error( 'http_request_failed', __( 'User has blocked requests through HTTP.' ) );
273
		}
274
275
		// If we are streaming to a file but no filename was given drop it in the WP temp dir
276
		// and pick its name using the basename of the $url
277
		if ( $r['stream'] ) {
278
			if ( empty( $r['filename'] ) ) {
279
				$r['filename'] = get_temp_dir() . basename( $url );
280
			}
281
282
			// Force some settings if we are streaming to a file and check for existence and perms of destination directory
283
			$r['blocking'] = true;
284
			if ( ! wp_is_writable( dirname( $r['filename'] ) ) ) {
285
				return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
286
			}
287
		}
288
289
		if ( is_null( $r['headers'] ) ) {
290
			$r['headers'] = array();
291
		}
292
293
		// WP allows passing in headers as a string, weirdly.
294
		if ( ! is_array( $r['headers'] ) ) {
295
			$processedHeaders = WP_Http::processHeaders( $r['headers'] );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
296
			$r['headers'] = $processedHeaders['headers'];
297
		}
298
299
		// Setup arguments
300
		$headers = $r['headers'];
301
		$data = $r['body'];
302
		$type = $r['method'];
303
		$options = array(
304
			'timeout' => $r['timeout'],
305
			'useragent' => $r['user-agent'],
306
			'blocking' => $r['blocking'],
307
			'hooks' => new Requests_Hooks(),
308
		);
309
310
		// Ensure redirects follow browser behaviour.
311
		$options['hooks']->register( 'requests.before_redirect', array( get_class(), 'browser_redirect_compatibility' ) );
312
313
		if ( $r['stream'] ) {
314
			$options['filename'] = $r['filename'];
315
		}
316
		if ( empty( $r['redirection'] ) ) {
317
			$options['follow_redirects'] = false;
318
		} else {
319
			$options['redirects'] = $r['redirection'];
320
		}
321
322
		// Use byte limit, if we can
323
		if ( isset( $r['limit_response_size'] ) ) {
324
			$options['max_bytes'] = $r['limit_response_size'];
325
		}
326
327
		// If we've got cookies, use and convert them to Requests_Cookie.
328
		if ( ! empty( $r['cookies'] ) ) {
329
			$options['cookies'] = WP_Http::normalize_cookies( $r['cookies'] );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
330
		}
331
332
		// SSL certificate handling
333
		if ( ! $r['sslverify'] ) {
334
			$options['verify'] = false;
335
		} else {
336
			$options['verify'] = $r['sslcertificates'];
337
		}
338
339
		// All non-GET/HEAD requests should put the arguments in the form body.
340
		if ( 'HEAD' !== $type && 'GET' !== $type ) {
341
			$options['data_format'] = 'body';
342
		}
343
344
		/**
345
		 * Filters whether SSL should be verified for non-local requests.
346
		 *
347
		 * @since 2.8.0
348
		 *
349
		 * @param bool $ssl_verify Whether to verify the SSL connection. Default true.
350
		 */
351
		$options['verify'] = apply_filters( 'https_ssl_verify', $options['verify'] );
352
353
		// Check for proxies.
354
		$proxy = new WP_HTTP_Proxy();
355
		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
356
			$options['proxy'] = new Requests_Proxy_HTTP( $proxy->host() . ':' . $proxy->port() );
0 ignored issues
show
Documentation introduced by
$proxy->host() . ':' . $proxy->port() is of type string, but the function expects a array|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
357
358
			if ( $proxy->use_authentication() ) {
359
				$options['proxy']->use_authentication = true;
360
				$options['proxy']->user = $proxy->username();
361
				$options['proxy']->pass = $proxy->password();
362
			}
363
		}
364
365
		try {
366
			$requests_response = Requests::request( $url, $headers, $data, $type, $options );
367
368
			// Convert the response into an array
369
			$http_response = new WP_HTTP_Requests_Response( $requests_response, $r['filename'] );
370
			$response = $http_response->to_array();
371
372
			// Add the original object to the array.
373
			$response['http_response'] = $http_response;
374
		}
375
		catch ( Requests_Exception $e ) {
376
			$response = new WP_Error( 'http_request_failed', $e->getMessage() );
377
		}
378
379
		/**
380
		 * Fires after an HTTP API response is received and before the response is returned.
381
		 *
382
		 * @since 2.8.0
383
		 *
384
		 * @param array|WP_Error $response HTTP response or WP_Error object.
385
		 * @param string         $context  Context under which the hook is fired.
386
		 * @param string         $class    HTTP transport used.
387
		 * @param array          $args     HTTP request arguments.
388
		 * @param string         $url      The request URL.
389
		 */
390
		do_action( 'http_api_debug', $response, 'response', 'Requests', $r, $url );
391
		if ( is_wp_error( $response ) ) {
392
			return $response;
393
		}
394
395
		if ( ! $r['blocking'] ) {
396
			return array(
397
				'headers' => array(),
398
				'body' => '',
399
				'response' => array(
400
					'code' => false,
401
					'message' => false,
402
				),
403
				'cookies' => array(),
404
				'http_response' => null,
405
			);
406
		}
407
408
		/**
409
		 * Filters the HTTP API response immediately before the response is returned.
410
		 *
411
		 * @since 2.9.0
412
		 *
413
		 * @param array  $response HTTP response.
414
		 * @param array  $r        HTTP request arguments.
415
		 * @param string $url      The request URL.
416
		 */
417
		return apply_filters( 'http_response', $response, $r, $url );
418
	}
419
420
	/**
421
	 * Normalizes cookies for using in Requests.
422
	 *
423
	 * @since 4.6.0
424
	 * @access public
425
	 * @static
426
	 *
427
	 * @param array $cookies List of cookies to send with the request.
428
	 * @return Requests_Cookie_Jar Cookie holder object.
429
	 */
430
	public static function normalize_cookies( $cookies ) {
431
		$cookie_jar = new Requests_Cookie_Jar();
432
433
		foreach ( $cookies as $name => $value ) {
434
			if ( $value instanceof WP_Http_Cookie ) {
435
				$cookie_jar[ $value->name ] = new Requests_Cookie( $value->name, $value->value, $value->get_attributes() );
436
			} elseif ( is_string( $value ) ) {
437
				$cookie_jar[ $name ] = new Requests_Cookie( $name, $value );
438
			}
439
		}
440
441
		return $cookie_jar;
442
	}
443
444
	/**
445
	 * Match redirect behaviour to browser handling.
446
	 *
447
	 * Changes 302 redirects from POST to GET to match browser handling. Per
448
	 * RFC 7231, user agents can deviate from the strict reading of the
449
	 * specification for compatibility purposes.
450
	 *
451
	 * @since 4.6.0
452
	 * @access public
453
	 * @static
454
	 *
455
	 * @param string            $location URL to redirect to.
456
	 * @param array             $headers  Headers for the redirect.
457
	 * @param array             $options  Redirect request options.
458
	 * @param Requests_Response $original Response object.
459
	 */
460
	public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
461
		// Browser compat
462
		if ( $original->status_code === 302 ) {
463
			$options['type'] = Requests::GET;
464
		}
465
	}
466
467
	/**
468
	 * Tests which transports are capable of supporting the request.
469
	 *
470
	 * @since 3.2.0
471
	 * @access public
472
	 *
473
	 * @param array $args Request arguments
474
	 * @param string $url URL to Request
475
	 *
476
	 * @return string|false Class name for the first transport that claims to support the request. False if no transport claims to support the request.
477
	 */
478
	public function _get_first_available_transport( $args, $url = null ) {
479
		$transports = array( 'curl', 'streams' );
480
481
		/**
482
		 * Filters which HTTP transports are available and in what order.
483
		 *
484
		 * @since 3.7.0
485
		 *
486
		 * @param array  $transports Array of HTTP transports to check. Default array contains
487
		 *                           'curl', and 'streams', in that order.
488
		 * @param array  $args       HTTP request arguments.
489
		 * @param string $url        The URL to request.
490
		 */
491
		$request_order = apply_filters( 'http_api_transports', $transports, $args, $url );
492
493
		// Loop over each transport on each HTTP request looking for one which will serve this request's needs.
494
		foreach ( $request_order as $transport ) {
495
			if ( in_array( $transport, $transports ) ) {
496
				$transport = ucfirst( $transport );
497
			}
498
			$class = 'WP_Http_' . $transport;
499
500
			// Check to see if this transport is a possibility, calls the transport statically.
501
			if ( !call_user_func( array( $class, 'test' ), $args, $url ) )
502
				continue;
503
504
			return $class;
505
		}
506
507
		return false;
508
	}
509
510
	/**
511
	 * Dispatches a HTTP request to a supporting transport.
512
	 *
513
	 * Tests each transport in order to find a transport which matches the request arguments.
514
	 * Also caches the transport instance to be used later.
515
	 *
516
	 * The order for requests is cURL, and then PHP Streams.
517
	 *
518
	 * @since 3.2.0
519
	 *
520
	 * @static
521
	 * @access private
522
	 *
523
	 * @param string $url URL to Request
524
	 * @param array $args Request arguments
525
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
526
	 */
527
	private function _dispatch_request( $url, $args ) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
528
		static $transports = array();
529
530
		$class = $this->_get_first_available_transport( $args, $url );
531
		if ( !$class )
532
			return new WP_Error( 'http_failure', __( 'There are no HTTP transports available which can complete the requested request.' ) );
533
534
		// Transport claims to support request, instantiate it and give it a whirl.
535
		if ( empty( $transports[$class] ) )
536
			$transports[$class] = new $class;
537
538
		$response = $transports[$class]->request( $url, $args );
539
540
		/** This action is documented in wp-includes/class-http.php */
541
		do_action( 'http_api_debug', $response, 'response', $class, $args, $url );
542
543
		if ( is_wp_error( $response ) )
544
			return $response;
545
546
		/**
547
		 * Filters the HTTP API response immediately before the response is returned.
548
		 *
549
		 * @since 2.9.0
550
		 *
551
		 * @param array  $response HTTP response.
552
		 * @param array  $args     HTTP request arguments.
553
		 * @param string $url      The request URL.
554
		 */
555
		return apply_filters( 'http_response', $response, $args, $url );
556
	}
557
558
	/**
559
	 * Uses the POST HTTP method.
560
	 *
561
	 * Used for sending data that is expected to be in the body.
562
	 *
563
	 * @access public
564
	 * @since 2.7.0
565
	 *
566
	 * @param string       $url  The request URL.
567
	 * @param string|array $args Optional. Override the defaults.
568
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
569
	 */
570
	public function post($url, $args = array()) {
571
		$defaults = array('method' => 'POST');
572
		$r = wp_parse_args( $args, $defaults );
573
		return $this->request($url, $r);
574
	}
575
576
	/**
577
	 * Uses the GET HTTP method.
578
	 *
579
	 * Used for sending data that is expected to be in the body.
580
	 *
581
	 * @access public
582
	 * @since 2.7.0
583
	 *
584
	 * @param string $url The request URL.
585
	 * @param string|array $args Optional. Override the defaults.
586
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
587
	 */
588
	public function get($url, $args = array()) {
589
		$defaults = array('method' => 'GET');
590
		$r = wp_parse_args( $args, $defaults );
591
		return $this->request($url, $r);
592
	}
593
594
	/**
595
	 * Uses the HEAD HTTP method.
596
	 *
597
	 * Used for sending data that is expected to be in the body.
598
	 *
599
	 * @access public
600
	 * @since 2.7.0
601
	 *
602
	 * @param string $url The request URL.
603
	 * @param string|array $args Optional. Override the defaults.
604
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
605
	 */
606
	public function head($url, $args = array()) {
607
		$defaults = array('method' => 'HEAD');
608
		$r = wp_parse_args( $args, $defaults );
609
		return $this->request($url, $r);
610
	}
611
612
	/**
613
	 * Parses the responses and splits the parts into headers and body.
614
	 *
615
	 * @access public
616
	 * @static
617
	 * @since 2.7.0
618
	 *
619
	 * @param string $strResponse The full response string
620
	 * @return array Array with 'headers' and 'body' keys.
621
	 */
622
	public static function processResponse($strResponse) {
623
		$res = explode("\r\n\r\n", $strResponse, 2);
624
625
		return array('headers' => $res[0], 'body' => isset($res[1]) ? $res[1] : '');
626
	}
627
628
	/**
629
	 * Transform header string into an array.
630
	 *
631
	 * If an array is given then it is assumed to be raw header data with numeric keys with the
632
	 * headers as the values. No headers must be passed that were already processed.
633
	 *
634
	 * @access public
635
	 * @static
636
	 * @since 2.7.0
637
	 *
638
	 * @param string|array $headers
639
	 * @param string $url The URL that was requested
640
	 * @return array Processed string headers. If duplicate headers are encountered,
641
	 * 					Then a numbered array is returned as the value of that header-key.
642
	 */
643
	public static function processHeaders( $headers, $url = '' ) {
644
		// Split headers, one per array element.
645
		if ( is_string($headers) ) {
646
			// Tolerate line terminator: CRLF = LF (RFC 2616 19.3).
647
			$headers = str_replace("\r\n", "\n", $headers);
648
			/*
649
			 * Unfold folded header fields. LWS = [CRLF] 1*( SP | HT ) <US-ASCII SP, space (32)>,
650
			 * <US-ASCII HT, horizontal-tab (9)> (RFC 2616 2.2).
651
			 */
652
			$headers = preg_replace('/\n[ \t]/', ' ', $headers);
653
			// Create the headers array.
654
			$headers = explode("\n", $headers);
655
		}
656
657
		$response = array('code' => 0, 'message' => '');
658
659
		/*
660
		 * If a redirection has taken place, The headers for each page request may have been passed.
661
		 * In this case, determine the final HTTP header and parse from there.
662
		 */
663
		for ( $i = count($headers)-1; $i >= 0; $i-- ) {
664
			if ( !empty($headers[$i]) && false === strpos($headers[$i], ':') ) {
665
				$headers = array_splice($headers, $i);
666
				break;
667
			}
668
		}
669
670
		$cookies = array();
671
		$newheaders = array();
672
		foreach ( (array) $headers as $tempheader ) {
673
			if ( empty($tempheader) )
674
				continue;
675
676
			if ( false === strpos($tempheader, ':') ) {
677
				$stack = explode(' ', $tempheader, 3);
678
				$stack[] = '';
679
				list( , $response['code'], $response['message']) = $stack;
680
				continue;
681
			}
682
683
			list($key, $value) = explode(':', $tempheader, 2);
684
685
			$key = strtolower( $key );
686
			$value = trim( $value );
687
688
			if ( isset( $newheaders[ $key ] ) ) {
689
				if ( ! is_array( $newheaders[ $key ] ) )
690
					$newheaders[$key] = array( $newheaders[ $key ] );
691
				$newheaders[ $key ][] = $value;
692
			} else {
693
				$newheaders[ $key ] = $value;
694
			}
695
			if ( 'set-cookie' == $key )
696
				$cookies[] = new WP_Http_Cookie( $value, $url );
697
		}
698
699
		// Cast the Response Code to an int
700
		$response['code'] = intval( $response['code'] );
701
702
		return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
703
	}
704
705
	/**
706
	 * Takes the arguments for a ::request() and checks for the cookie array.
707
	 *
708
	 * If it's found, then it upgrades any basic name => value pairs to WP_Http_Cookie instances,
709
	 * which are each parsed into strings and added to the Cookie: header (within the arguments array).
710
	 * Edits the array by reference.
711
	 *
712
	 * @access public
713
	 * @version 2.8.0
714
	 * @static
715
	 *
716
	 * @param array $r Full array of args passed into ::request()
717
	 */
718
	public static function buildCookieHeader( &$r ) {
719
		if ( ! empty($r['cookies']) ) {
720
			// Upgrade any name => value cookie pairs to WP_HTTP_Cookie instances.
721
			foreach ( $r['cookies'] as $name => $value ) {
722
				if ( ! is_object( $value ) )
723
					$r['cookies'][ $name ] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) );
724
			}
725
726
			$cookies_header = '';
727
			foreach ( (array) $r['cookies'] as $cookie ) {
728
				$cookies_header .= $cookie->getHeaderValue() . '; ';
729
			}
730
731
			$cookies_header = substr( $cookies_header, 0, -2 );
732
			$r['headers']['cookie'] = $cookies_header;
733
		}
734
	}
735
736
	/**
737
	 * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
738
	 *
739
	 * Based off the HTTP http_encoding_dechunk function.
740
	 *
741
	 * @link https://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding.
742
	 *
743
	 * @access public
744
	 * @since 2.7.0
745
	 * @static
746
	 *
747
	 * @param string $body Body content
748
	 * @return string Chunked decoded body on success or raw body on failure.
749
	 */
750
	public static function chunkTransferDecode( $body ) {
751
		// The body is not chunked encoded or is malformed.
752
		if ( ! preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', trim( $body ) ) )
753
			return $body;
754
755
		$parsed_body = '';
756
757
		// We'll be altering $body, so need a backup in case of error.
758
		$body_original = $body;
759
760
		while ( true ) {
761
			$has_chunk = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $body, $match );
762
			if ( ! $has_chunk || empty( $match[1] ) )
763
				return $body_original;
764
765
			$length = hexdec( $match[1] );
766
			$chunk_length = strlen( $match[0] );
767
768
			// Parse out the chunk of data.
769
			$parsed_body .= substr( $body, $chunk_length, $length );
770
771
			// Remove the chunk from the raw data.
772
			$body = substr( $body, $length + $chunk_length );
773
774
			// End of the document.
775
			if ( '0' === trim( $body ) )
776
				return $parsed_body;
777
		}
778
	}
779
780
	/**
781
	 * Block requests through the proxy.
782
	 *
783
	 * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will
784
	 * prevent plugins from working and core functionality, if you don't include api.wordpress.org.
785
	 *
786
	 * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL as true in your wp-config.php
787
	 * file and this will only allow localhost and your site to make requests. The constant
788
	 * WP_ACCESSIBLE_HOSTS will allow additional hosts to go through for requests. The format of the
789
	 * WP_ACCESSIBLE_HOSTS constant is a comma separated list of hostnames to allow, wildcard domains
790
	 * are supported, eg *.wordpress.org will allow for all subdomains of wordpress.org to be contacted.
791
	 *
792
	 * @since 2.8.0
793
	 * @link https://core.trac.wordpress.org/ticket/8927 Allow preventing external requests.
794
	 * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_ACCESSIBLE_HOSTS
795
	 *
796
	 * @staticvar array|null $accessible_hosts
797
	 * @staticvar array      $wildcard_regex
798
	 *
799
	 * @param string $uri URI of url.
800
	 * @return bool True to block, false to allow.
801
	 */
802
	public function block_request($uri) {
803
		// We don't need to block requests, because nothing is blocked.
804
		if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL )
805
			return false;
806
807
		$check = parse_url($uri);
808
		if ( ! $check )
809
			return true;
810
811
		$home = parse_url( get_option('siteurl') );
812
813
		// Don't block requests back to ourselves by default.
814 View Code Duplication
		if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) {
815
			/**
816
			 * Filters whether to block local requests through the proxy.
817
			 *
818
			 * @since 2.8.0
819
			 *
820
			 * @param bool $block Whether to block local requests through proxy.
821
			 *                    Default false.
822
			 */
823
			return apply_filters( 'block_local_requests', false );
824
		}
825
826
		if ( !defined('WP_ACCESSIBLE_HOSTS') )
827
			return true;
828
829
		static $accessible_hosts = null;
830
		static $wildcard_regex = array();
831 View Code Duplication
		if ( null === $accessible_hosts ) {
832
			$accessible_hosts = preg_split('|,\s*|', WP_ACCESSIBLE_HOSTS);
833
834
			if ( false !== strpos(WP_ACCESSIBLE_HOSTS, '*') ) {
835
				$wildcard_regex = array();
836
				foreach ( $accessible_hosts as $host )
837
					$wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) );
838
				$wildcard_regex = '/^(' . implode('|', $wildcard_regex) . ')$/i';
839
			}
840
		}
841
842 View Code Duplication
		if ( !empty($wildcard_regex) )
843
			return !preg_match($wildcard_regex, $check['host']);
844
		else
845
			return !in_array( $check['host'], $accessible_hosts ); //Inverse logic, If it's in the array, then we can't access it.
846
847
	}
848
849
	/**
850
	 * Used as a wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7.
851
	 *
852
	 * @access protected
853
	 * @deprecated 4.4.0 Use wp_parse_url()
854
	 * @see wp_parse_url()
855
	 *
856
	 * @param string $url The URL to parse.
857
	 * @return bool|array False on failure; Array of URL components on success;
858
	 *                    See parse_url()'s return values.
859
	 */
860
	protected static function parse_url( $url ) {
861
		_deprecated_function( __METHOD__, '4.4.0', 'wp_parse_url()' );
862
		return wp_parse_url( $url );
863
	}
864
865
	/**
866
	 * Converts a relative URL to an absolute URL relative to a given URL.
867
	 *
868
	 * If an Absolute URL is provided, no processing of that URL is done.
869
	 *
870
	 * @since 3.4.0
871
	 *
872
	 * @static
873
	 * @access public
874
	 *
875
	 * @param string $maybe_relative_path The URL which might be relative
876
	 * @param string $url                 The URL which $maybe_relative_path is relative to
877
	 * @return string An Absolute URL, in a failure condition where the URL cannot be parsed, the relative URL will be returned.
878
	 */
879
	public static function make_absolute_url( $maybe_relative_path, $url ) {
880
		if ( empty( $url ) )
881
			return $maybe_relative_path;
882
883
		if ( ! $url_parts = wp_parse_url( $url ) ) {
884
			return $maybe_relative_path;
885
		}
886
887
		if ( ! $relative_url_parts = wp_parse_url( $maybe_relative_path ) ) {
888
			return $maybe_relative_path;
889
		}
890
891
		// Check for a scheme on the 'relative' url
892
		if ( ! empty( $relative_url_parts['scheme'] ) ) {
893
			return $maybe_relative_path;
894
		}
895
896
		$absolute_path = $url_parts['scheme'] . '://';
897
898
		// Schemeless URL's will make it this far, so we check for a host in the relative url and convert it to a protocol-url
899
		if ( isset( $relative_url_parts['host'] ) ) {
900
			$absolute_path .= $relative_url_parts['host'];
901
			if ( isset( $relative_url_parts['port'] ) )
902
				$absolute_path .= ':' . $relative_url_parts['port'];
903
		} else {
904
			$absolute_path .= $url_parts['host'];
905
			if ( isset( $url_parts['port'] ) )
906
				$absolute_path .= ':' . $url_parts['port'];
907
		}
908
909
		// Start off with the Absolute URL path.
910
		$path = ! empty( $url_parts['path'] ) ? $url_parts['path'] : '/';
911
912
		// If it's a root-relative path, then great.
913
		if ( ! empty( $relative_url_parts['path'] ) && '/' == $relative_url_parts['path'][0] ) {
914
			$path = $relative_url_parts['path'];
915
916
		// Else it's a relative path.
917
		} elseif ( ! empty( $relative_url_parts['path'] ) ) {
918
			// Strip off any file components from the absolute path.
919
			$path = substr( $path, 0, strrpos( $path, '/' ) + 1 );
920
921
			// Build the new path.
922
			$path .= $relative_url_parts['path'];
923
924
			// Strip all /path/../ out of the path.
925
			while ( strpos( $path, '../' ) > 1 ) {
926
				$path = preg_replace( '![^/]+/\.\./!', '', $path );
927
			}
928
929
			// Strip any final leading ../ from the path.
930
			$path = preg_replace( '!^/(\.\./)+!', '', $path );
931
		}
932
933
		// Add the Query string.
934
		if ( ! empty( $relative_url_parts['query'] ) )
935
			$path .= '?' . $relative_url_parts['query'];
936
937
		return $absolute_path . '/' . ltrim( $path, '/' );
938
	}
939
940
	/**
941
	 * Handles HTTP Redirects and follows them if appropriate.
942
	 *
943
	 * @since 3.7.0
944
	 *
945
	 * @static
946
	 *
947
	 * @param string $url The URL which was requested.
948
	 * @param array $args The Arguments which were used to make the request.
949
	 * @param array $response The Response of the HTTP request.
950
	 * @return false|object False if no redirect is present, a WP_HTTP or WP_Error result otherwise.
951
	 */
952
	public static function handle_redirects( $url, $args, $response ) {
953
		// If no redirects are present, or, redirects were not requested, perform no action.
954
		if ( ! isset( $response['headers']['location'] ) || 0 === $args['_redirection'] )
955
			return false;
956
957
		// Only perform redirections on redirection http codes.
958
		if ( $response['response']['code'] > 399 || $response['response']['code'] < 300 )
959
			return false;
960
961
		// Don't redirect if we've run out of redirects.
962
		if ( $args['redirection']-- <= 0 )
963
			return new WP_Error( 'http_request_failed', __('Too many redirects.') );
964
965
		$redirect_location = $response['headers']['location'];
966
967
		// If there were multiple Location headers, use the last header specified.
968
		if ( is_array( $redirect_location ) )
969
			$redirect_location = array_pop( $redirect_location );
970
971
		$redirect_location = WP_Http::make_absolute_url( $redirect_location, $url );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
972
973
		// POST requests should not POST to a redirected location.
974
		if ( 'POST' == $args['method'] ) {
975
			if ( in_array( $response['response']['code'], array( 302, 303 ) ) )
976
				$args['method'] = 'GET';
977
		}
978
979
		// Include valid cookies in the redirect process.
980
		if ( ! empty( $response['cookies'] ) ) {
981
			foreach ( $response['cookies'] as $cookie ) {
982
				if ( $cookie->test( $redirect_location ) )
983
					$args['cookies'][] = $cookie;
984
			}
985
		}
986
987
		return wp_remote_request( $redirect_location, $args );
988
	}
989
990
	/**
991
	 * Determines if a specified string represents an IP address or not.
992
	 *
993
	 * This function also detects the type of the IP address, returning either
994
	 * '4' or '6' to represent a IPv4 and IPv6 address respectively.
995
	 * This does not verify if the IP is a valid IP, only that it appears to be
996
	 * an IP address.
997
	 *
998
	 * @link http://home.deds.nl/~aeron/regex/ for IPv6 regex
999
	 *
1000
	 * @since 3.7.0
1001
	 * @static
1002
	 *
1003
	 * @param string $maybe_ip A suspected IP address
1004
	 * @return integer|bool Upon success, '4' or '6' to represent a IPv4 or IPv6 address, false upon failure
1005
	 */
1006
	public static function is_ip_address( $maybe_ip ) {
1007
		if ( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $maybe_ip ) )
1008
			return 4;
1009
1010
		if ( false !== strpos( $maybe_ip, ':' ) && preg_match( '/^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i', trim( $maybe_ip, ' []' ) ) )
1011
			return 6;
1012
1013
		return false;
1014
	}
1015
1016
}
1017