[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/ -> class-wp-http-curl.php (source)

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


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7