[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * HTTP API: WP_Http_Curl class 4 * 5 * @package WordPress 6 * @subpackage HTTP 7 * @since 4.4.0 8 */ 9 10 /** 11 * Core class used to integrate Curl as an HTTP transport. 12 * 13 * HTTP request method uses Curl extension to retrieve the url. 14 * 15 * Requires the Curl extension to be installed. 16 * 17 * @since 2.7.0 18 * @deprecated 6.4.0 Use WP_Http 19 * @see WP_Http 20 */ 21 #[AllowDynamicProperties] 22 class WP_Http_Curl { 23 24 /** 25 * Temporary header storage for during requests. 26 * 27 * @since 3.2.0 28 * @var string 29 */ 30 private $headers = ''; 31 32 /** 33 * Temporary body storage for during requests. 34 * 35 * @since 3.6.0 36 * @var string 37 */ 38 private $body = ''; 39 40 /** 41 * The maximum amount of data to receive from the remote server. 42 * 43 * @since 3.6.0 44 * @var int|false 45 */ 46 private $max_body_length = false; 47 48 /** 49 * The file resource used for streaming to file. 50 * 51 * @since 3.6.0 52 * @var resource|false 53 */ 54 private $stream_handle = false; 55 56 /** 57 * The total bytes written in the current request. 58 * 59 * @since 4.1.0 60 * @var int 61 */ 62 private $bytes_written_total = 0; 63 64 /** 65 * Send a HTTP request to a URI using cURL extension. 66 * 67 * @since 2.7.0 68 * 69 * @param string $url The request URL. 70 * @param string|array $args Optional. Override the defaults. 71 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error 72 */ 73 public function request( $url, $args = array() ) { 74 $defaults = array( 75 'method' => 'GET', 76 'timeout' => 5, 77 'redirection' => 5, 78 'httpversion' => '1.0', 79 'blocking' => true, 80 'headers' => array(), 81 'body' => null, 82 'cookies' => array(), 83 'decompress' => false, 84 'stream' => false, 85 'filename' => null, 86 ); 87 88 $parsed_args = wp_parse_args( $args, $defaults ); 89 90 if ( isset( $parsed_args['headers']['User-Agent'] ) ) { 91 $parsed_args['user-agent'] = $parsed_args['headers']['User-Agent']; 92 unset( $parsed_args['headers']['User-Agent'] ); 93 } elseif ( isset( $parsed_args['headers']['user-agent'] ) ) { 94 $parsed_args['user-agent'] = $parsed_args['headers']['user-agent']; 95 unset( $parsed_args['headers']['user-agent'] ); 96 } 97 98 // Construct Cookie: header if any cookies are set. 99 WP_Http::buildCookieHeader( $parsed_args ); 100 101 $handle = curl_init(); 102 103 // cURL offers really easy proxy support. 104 $proxy = new WP_HTTP_Proxy(); 105 106 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { 107 108 curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); 109 curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() ); 110 curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() ); 111 112 if ( $proxy->use_authentication() ) { 113 curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); 114 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); 115 } 116 } 117 118 $is_local = isset( $parsed_args['local'] ) && $parsed_args['local']; 119 $ssl_verify = isset( $parsed_args['sslverify'] ) && $parsed_args['sslverify']; 120 if ( $is_local ) { 121 /** This filter is documented in wp-includes/class-wp-http-streams.php */ 122 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url ); 123 } elseif ( ! $is_local ) { 124 /** This filter is documented in wp-includes/class-wp-http.php */ 125 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url ); 126 } 127 128 /* 129 * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since. 130 * a value of 0 will allow an unlimited timeout. 131 */ 132 $timeout = (int) ceil( $parsed_args['timeout'] ); 133 curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout ); 134 curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout ); 135 136 curl_setopt( $handle, CURLOPT_URL, $url ); 137 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); 138 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( true === $ssl_verify ) ? 2 : false ); 139 curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); 140 141 if ( $ssl_verify ) { 142 curl_setopt( $handle, CURLOPT_CAINFO, $parsed_args['sslcertificates'] ); 143 } 144 145 curl_setopt( $handle, CURLOPT_USERAGENT, $parsed_args['user-agent'] ); 146 147 /* 148 * The option doesn't work with safe mode or when open_basedir is set, and there's 149 * a bug #17490 with redirected POST requests, so handle redirections outside Curl. 150 */ 151 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); 152 curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); 153 154 switch ( $parsed_args['method'] ) { 155 case 'HEAD': 156 curl_setopt( $handle, CURLOPT_NOBODY, true ); 157 break; 158 case 'POST': 159 curl_setopt( $handle, CURLOPT_POST, true ); 160 curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] ); 161 break; 162 case 'PUT': 163 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); 164 curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] ); 165 break; 166 default: 167 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $parsed_args['method'] ); 168 if ( ! is_null( $parsed_args['body'] ) ) { 169 curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] ); 170 } 171 break; 172 } 173 174 if ( true === $parsed_args['blocking'] ) { 175 curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); 176 curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) ); 177 } 178 179 curl_setopt( $handle, CURLOPT_HEADER, false ); 180 181 if ( isset( $parsed_args['limit_response_size'] ) ) { 182 $this->max_body_length = (int) $parsed_args['limit_response_size']; 183 } else { 184 $this->max_body_length = false; 185 } 186 187 // If streaming to a file open a file handle, and setup our curl streaming handler. 188 if ( $parsed_args['stream'] ) { 189 if ( ! WP_DEBUG ) { 190 $this->stream_handle = @fopen( $parsed_args['filename'], 'w+' ); 191 } else { 192 $this->stream_handle = fopen( $parsed_args['filename'], 'w+' ); 193 } 194 if ( ! $this->stream_handle ) { 195 return new WP_Error( 196 'http_request_failed', 197 sprintf( 198 /* translators: 1: fopen(), 2: File name. */ 199 __( 'Could not open handle for %1$s to %2$s.' ), 200 'fopen()', 201 $parsed_args['filename'] 202 ) 203 ); 204 } 205 } else { 206 $this->stream_handle = false; 207 } 208 209 if ( ! empty( $parsed_args['headers'] ) ) { 210 // cURL expects full header strings in each element. 211 $headers = array(); 212 foreach ( $parsed_args['headers'] as $name => $value ) { 213 $headers[] = "{$name}: $value"; 214 } 215 curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); 216 } 217 218 if ( '1.0' === $parsed_args['httpversion'] ) { 219 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); 220 } else { 221 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); 222 } 223 224 /** 225 * Fires before the cURL request is executed. 226 * 227 * Cookies are not currently handled by the HTTP API. This action allows 228 * plugins to handle cookies themselves. 229 * 230 * @since 2.8.0 231 * 232 * @param resource $handle The cURL handle returned by curl_init() (passed by reference). 233 * @param array $parsed_args The HTTP request arguments. 234 * @param string $url The request URL. 235 */ 236 do_action_ref_array( 'http_api_curl', array( &$handle, $parsed_args, $url ) ); 237 238 // We don't need to return the body, so don't. Just execute request and return. 239 if ( ! $parsed_args['blocking'] ) { 240 curl_exec( $handle ); 241 242 $curl_error = curl_error( $handle ); 243 if ( $curl_error ) { 244 curl_close( $handle ); 245 return new WP_Error( 'http_request_failed', $curl_error ); 246 } 247 if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) { 248 curl_close( $handle ); 249 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); 250 } 251 252 curl_close( $handle ); 253 return array( 254 'headers' => array(), 255 'body' => '', 256 'response' => array( 257 'code' => false, 258 'message' => false, 259 ), 260 'cookies' => array(), 261 ); 262 } 263 264 curl_exec( $handle ); 265 266 $processed_headers = WP_Http::processHeaders( $this->headers, $url ); 267 $body = $this->body; 268 $bytes_written_total = $this->bytes_written_total; 269 270 $this->headers = ''; 271 $this->body = ''; 272 $this->bytes_written_total = 0; 273 274 $curl_error = curl_errno( $handle ); 275 276 // If an error occurred, or, no response. 277 if ( $curl_error || ( 0 === strlen( $body ) && empty( $processed_headers['headers'] ) ) ) { 278 if ( CURLE_WRITE_ERROR /* 23 */ === $curl_error ) { 279 if ( ! $this->max_body_length || $this->max_body_length !== $bytes_written_total ) { 280 if ( $parsed_args['stream'] ) { 281 curl_close( $handle ); 282 fclose( $this->stream_handle ); 283 return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); 284 } else { 285 curl_close( $handle ); 286 return new WP_Error( 'http_request_failed', curl_error( $handle ) ); 287 } 288 } 289 } else { 290 $curl_error = curl_error( $handle ); 291 if ( $curl_error ) { 292 curl_close( $handle ); 293 return new WP_Error( 'http_request_failed', $curl_error ); 294 } 295 } 296 if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) { 297 curl_close( $handle ); 298 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); 299 } 300 } 301 302 curl_close( $handle ); 303 304 if ( $parsed_args['stream'] ) { 305 fclose( $this->stream_handle ); 306 } 307 308 $response = array( 309 'headers' => $processed_headers['headers'], 310 'body' => null, 311 'response' => $processed_headers['response'], 312 'cookies' => $processed_headers['cookies'], 313 'filename' => $parsed_args['filename'], 314 ); 315 316 // Handle redirects. 317 $redirect_response = WP_Http::handle_redirects( $url, $parsed_args, $response ); 318 if ( false !== $redirect_response ) { 319 return $redirect_response; 320 } 321 322 if ( true === $parsed_args['decompress'] 323 && true === WP_Http_Encoding::should_decode( $processed_headers['headers'] ) 324 ) { 325 $body = WP_Http_Encoding::decompress( $body ); 326 } 327 328 $response['body'] = $body; 329 330 return $response; 331 } 332 333 /** 334 * Grabs the headers of the cURL request. 335 * 336 * Each header is sent individually to this callback, and is appended to the `$header` property 337 * for temporary storage. 338 * 339 * @since 3.2.0 340 * 341 * @param resource $handle cURL handle. 342 * @param string $headers cURL request headers. 343 * @return int Length of the request headers. 344 */ 345 private function stream_headers( $handle, $headers ) { 346 $this->headers .= $headers; 347 return strlen( $headers ); 348 } 349 350 /** 351 * Grabs the body of the cURL request. 352 * 353 * The contents of the document are passed in chunks, and are appended to the `$body` 354 * property for temporary storage. Returning a length shorter than the length of 355 * `$data` passed in will cause cURL to abort the request with `CURLE_WRITE_ERROR`. 356 * 357 * @since 3.6.0 358 * 359 * @param resource $handle cURL handle. 360 * @param string $data cURL request body. 361 * @return int Total bytes of data written. 362 */ 363 private function stream_body( $handle, $data ) { 364 $data_length = strlen( $data ); 365 366 if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { 367 $data_length = ( $this->max_body_length - $this->bytes_written_total ); 368 $data = substr( $data, 0, $data_length ); 369 } 370 371 if ( $this->stream_handle ) { 372 $bytes_written = fwrite( $this->stream_handle, $data ); 373 } else { 374 $this->body .= $data; 375 $bytes_written = $data_length; 376 } 377 378 $this->bytes_written_total += $bytes_written; 379 380 // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR. 381 return $bytes_written; 382 } 383 384 /** 385 * Determines whether this class can be used for retrieving a URL. 386 * 387 * @since 2.7.0 388 * 389 * @param array $args Optional. Array of request arguments. Default empty array. 390 * @return bool False means this class can not be used, true means it can. 391 */ 392 public static function test( $args = array() ) { 393 if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) { 394 return false; 395 } 396 397 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; 398 399 if ( $is_ssl ) { 400 $curl_version = curl_version(); 401 // Check whether this cURL version support SSL requests. 402 if ( ! ( CURL_VERSION_SSL & $curl_version['features'] ) ) { 403 return false; 404 } 405 } 406 407 /** 408 * Filters whether cURL can be used as a transport for retrieving a URL. 409 * 410 * @since 2.7.0 411 * 412 * @param bool $use_class Whether the class can be used. Default true. 413 * @param array $args An array of request arguments. 414 */ 415 return apply_filters( 'use_curl_transport', true, $args ); 416 } 417 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |