解释 `wp_remote_get()` 函数的源码,它是如何处理远程 HTTP 请求的?

各位技术大牛们,大家好!我是今天的主讲人,很高兴能在这里和大家一起深入研究 WordPress 核心函数 wp_remote_get() 的源码。今天咱们就来扒一扒它的皮,看看它到底是如何处理远程 HTTP 请求的,让大家对它有一个更加清晰和深入的了解。

开场白:HTTP,你好!

在Web开发的世界里,HTTP协议就像一位默默奉献的邮递员,负责在客户端(比如我们的浏览器)和服务器之间传递信息。而wp_remote_get() 函数,就是WordPress世界里的一位熟练的邮递员,专门负责向远程服务器发出“取件”请求(GET请求),然后把取回来的“包裹”(HTTP响应)交给我们处理。

正文:解剖 wp_remote_get()

wp_remote_get() 函数实际上是 wp_remote_request() 函数的一个简化版本,它专门用于发送 GET 请求。wp_remote_request() 函数才是真正的幕后英雄,负责处理各种 HTTP 请求(GET, POST, PUT, DELETE等)。所以,要彻底了解 wp_remote_get(),我们必须先从 wp_remote_request() 入手。

1. wp_remote_request():请求的起点

wp_remote_request() 函数的原型如下:

function wp_remote_request( $url, $args = array() ) {
    global $wp_version;

    $defaults = array(
        'method'      => 'GET',
        'timeout'     => 5,
        'redirection' => 5,
        'httpversion' => '1.0',
        'user-agent'  => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ),
        'reject_unsafe_urls' => true,
        'blocking'    => true,
        'headers'     => array(),
        'body'        => null,
        'cookies'     => array(),
        'filename'    => null,
        'stream'      => false, // Add support for streaming response body
        'sslverify'   => true, // Verify SSL certificates
        'sslcertificates' => false, // Set custom SSL certificates path
        'compress'    => false, // Should we compress the body?
        'decompress'  => true, // Should we auto-decompress the response?
        'limit_response_size' => null // Maximum response body size to download
    );

    $args = wp_parse_args( $args, $defaults );

    //... (更多代码)
}
  • $url: 这是我们要请求的远程 URL,邮递员要去的地址。
  • $args: 这是一个数组,包含了各种请求参数,比如请求方法、超时时间、HTTP版本、用户代理等等。

重点参数解析:

参数名 描述 默认值
method HTTP 请求方法(GET, POST, PUT, DELETE 等)。 GET
timeout 请求超时时间,单位是秒。如果超过这个时间没有收到响应,就会返回一个错误。 5
redirection 最大重定向次数。如果服务器返回重定向响应(比如 301, 302),wp_remote_request() 会自动重定向,直到达到这个最大次数为止。 5
httpversion 使用的 HTTP 协议版本(1.0 或 1.1)。 1.0
user-agent User-Agent 头部,用来标识请求的客户端。WordPress 默认会设置为 WordPress/版本号; 站点URL WordPress/版本号; 站点URL
blocking 是否阻塞请求。如果设置为 true,函数会一直等待直到收到响应才返回。如果设置为 false,函数会立即返回,请求会在后台执行。 true
headers 一个关联数组,包含了要发送的 HTTP 头部。 array()
body 请求体,用于 POST, PUT 等请求。 null
cookies 一个数组,包含了要发送的 cookies。 array()
filename 如果要将响应保存到文件中,可以指定文件名。 null
stream 是否流式传输响应体。如果设置为 true,响应体将以流的方式读取,而不是一次性加载到内存中。这对于处理大型响应非常有用。 false
sslverify 是否验证 SSL 证书。强烈建议设置为 true,以确保连接的安全性。 true
sslcertificates 自定义SSL证书路径。 false
compress 是否对请求体进行压缩。 false
decompress 是否自动解压缩响应体。 true
limit_response_size 限制下载响应体的大小。 null

2. 选择合适的传输方式

wp_remote_request() 函数会根据当前环境选择合适的 HTTP 传输方式。它会依次尝试以下几种方式:

  1. Streams: 如果 PHP 启用了 allow_url_fopen,并且 URL 是 HTTP 或 HTTPS,那么会使用 PHP 的流函数来处理请求。
  2. cURL: 如果 PHP 启用了 cURL 扩展,那么会使用 cURL 来处理请求。这是最常用和推荐的方式,因为它功能强大且稳定。
  3. Fsockopen: 如果以上两种方式都不可用,那么会使用 fsockopen() 函数来建立 socket 连接,手动发送 HTTP 请求。
// Determine which transport to use.
$transports = array( 'streams', 'curl', 'fsockopen' );

// Filter the transports array.
$transports = apply_filters( 'http_api_transports', $transports, $url, $args );

// Find a suitable transport.
$response = false;
foreach ( $transports as $transport ) {
    $class = 'WP_HTTP_' . ucfirst( $transport );

    if ( ! class_exists( $class ) ) {
        continue;
    }

    $http = new $class();

    if ( ! $http->test( $url ) ) {
        continue;
    }

    $response = $http->request( $url, $args );

    if ( ! is_wp_error( $response ) ) {
        break;
    }
}

这段代码会遍历 transports 数组,依次尝试每种传输方式。如果某种方式可用并且成功发送了请求,那么就跳出循环,不再尝试其他方式。

3. 使用 Streams (WP_HTTP_Streams)

如果选择了 Streams 方式,WP_HTTP_Streams 类会使用 PHP 的流函数来处理请求。

class WP_HTTP_Streams {
    public function request( $url, $args = array() ) {
        //...
        $stream_options = array(
            'http' => array(
                'method'           => $method,
                'user_agent'       => $args['user-agent'],
                'max_redirects'    => $args['redirection'],
                'timeout'          => $args['timeout'],
                'ignore_errors'    => true, // Return content even on errors
                'protocol_version' => $args['httpversion'],
                'header'           => $headers,
                'content'          => $body,
            ),
            'ssl' => array(
                'verify_peer'      => $args['sslverify'],
                'verify_peer_name' => $args['sslverify'],
            ),
        );

        $context = stream_context_create( $stream_options );
        $stream = @fopen( $url, 'r', false, $context );

        if ( ! $stream ) {
            return new WP_Error( 'http_request_failed', __( 'Failed to open stream' ) );
        }

        $response = stream_get_contents( $stream );
        $meta = stream_get_meta_data( $stream );

        fclose( $stream );
        //...
    }
}

这段代码会创建一个 stream context,包含了请求的各种参数,然后使用 fopen() 函数打开 URL,读取响应内容。

4. 使用 cURL (WP_HTTP_Curl)

如果选择了 cURL 方式,WP_HTTP_Curl 类会使用 cURL 扩展来处理请求。

class WP_HTTP_Curl {
    public function request( $url, $args = array() ) {
        $ch = curl_init();

        curl_setopt( $ch, CURLOPT_URL, $url );
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
        curl_setopt( $ch, CURLOPT_HEADER, true );
        curl_setopt( $ch, CURLOPT_USERAGENT, $args['user-agent'] );
        curl_setopt( $ch, CURLOPT_TIMEOUT, $args['timeout'] );
        curl_setopt( $ch, CURLOPT_MAXREDIRS, $args['redirection'] );
        curl_setopt( $ch, CURLOPT_HTTP_VERSION, ($args['httpversion'] == '1.1') ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0 );
        curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, $args['sslverify'] );
        curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, $args['sslverify'] ? 2 : 0 );

        if ( 'POST' == $method || 'PUT' == $method ) {
            curl_setopt( $ch, CURLOPT_POST, true );
            curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['body'] );
        }

        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );

        $response = curl_exec( $ch );

        if ( false === $response ) {
            $error_message = curl_error( $ch );
            $error_code = curl_errno( $ch );
            curl_close( $ch );
            return new WP_Error( 'http_request_failed', $error_message, $error_code );
        }

        $header_size = curl_getinfo( $ch, CURLINFO_HEADER_SIZE );
        $header = substr( $response, 0, $header_size );
        $body = substr( $response, $header_size );

        curl_close( $ch );
        //...
    }
}

这段代码会初始化一个 cURL 句柄,设置各种选项,然后使用 curl_exec() 函数发送请求,获取响应内容。

5. 使用 Fsockopen (WP_HTTP_Fsockopen)

如果选择了 Fsockopen 方式,WP_HTTP_Fsockopen 类会使用 fsockopen() 函数来处理请求。

class WP_HTTP_Fsockopen {
    public function request( $url, $args = array() ) {
        $ssl = substr( $url, 0, 8 ) == 'https://';
        $port = $ssl ? 443 : 80;

        $host = parse_url( $url, PHP_URL_HOST );
        $path = parse_url( $url, PHP_URL_PATH );
        $query = parse_url( $url, PHP_URL_QUERY );

        if ( $query ) {
            $path .= '?' . $query;
        }

        $fp = fsockopen( ($ssl ? 'ssl://' : '') . $host, $port, $errno, $errstr, $args['timeout'] );

        if ( ! $fp ) {
            return new WP_Error( 'http_request_failed', $errstr, $errno );
        }

        $request = $method . ' ' . $path . ' HTTP/' . $args['httpversion'] . "rn";
        $request .= "Host: " . $host . "rn";
        $request .= implode( "rn", $headers ) . "rnrn";
        $request .= $body;

        fwrite( $fp, $request );

        $response = '';
        while ( ! feof( $fp ) ) {
            $response .= fgets( $fp, 1160 );
        }

        fclose( $fp );
        //...
    }
}

这段代码会使用 fsockopen() 函数建立 socket 连接,手动构建 HTTP 请求,然后发送请求,读取响应内容。

6. 处理响应

无论使用哪种传输方式,wp_remote_request() 函数都会将响应内容解析成一个数组,包含了以下几个元素:

  • body: 响应体。
  • headers: 响应头,是一个关联数组。
  • response: 一个数组,包含了 HTTP 状态码和状态信息。
  • cookies: 一个数组,包含了响应中的 cookies。
  • filename: 如果指定了文件名,那么这个元素会包含文件名。
$response = array(
    'headers'  => $headers,
    'body'     => $body,
    'response' => array( 'code' => $status_code, 'message' => $status_text ),
    'cookies'  => $cookies,
    'filename' => $args['filename'],
);

7. wp_remote_get():GET 请求的便捷方式

现在我们再来看看 wp_remote_get() 函数。它实际上只是 wp_remote_request() 函数的一个简单封装,专门用于发送 GET 请求。

function wp_remote_get( $url, $args = array() ) {
    $defaults = array( 'method' => 'GET' );
    $args = wp_parse_args( $args, $defaults );
    return wp_remote_request( $url, $args );
}

可以看到,wp_remote_get() 函数只是简单地将 method 参数设置为 GET,然后调用 wp_remote_request() 函数。

8. 错误处理

wp_remote_request() 函数会使用 WP_Error 对象来处理错误。如果请求失败,函数会返回一个 WP_Error 对象,包含了错误代码和错误信息。

if ( is_wp_error( $response ) ) {
    // Handle the error
    $error_code = $response->get_error_code();
    $error_message = $response->get_error_message();
    echo "Error: $error_code - $error_message";
} else {
    // Process the response
    $body = wp_remote_retrieve_body( $response );
    //...
}

9. 实际应用

让我们来看一个实际的例子,使用 wp_remote_get() 函数获取一个网页的内容:

$url = 'https://www.example.com';
$response = wp_remote_get( $url );

if ( is_wp_error( $response ) ) {
    $error_message = $response->get_error_message();
    echo "Something went wrong: $error_message";
} else {
    $body = wp_remote_retrieve_body( $response );
    echo $body;
}

这段代码会向 https://www.example.com 发送一个 GET 请求,如果请求成功,就会输出网页的内容。

10. 其他相关函数

  • wp_remote_post(): 用于发送 POST 请求。
  • wp_remote_head(): 用于发送 HEAD 请求。
  • wp_remote_retrieve_body(): 用于从响应中提取响应体。
  • wp_remote_retrieve_headers(): 用于从响应中提取响应头。
  • wp_remote_retrieve_response_code(): 用于从响应中提取 HTTP 状态码。
  • wp_remote_retrieve_response_message(): 用于从响应中提取 HTTP 状态信息。

总结:HTTP 请求的艺术

wp_remote_get()wp_remote_request() 函数是 WordPress 中处理远程 HTTP 请求的核心函数。它们提供了灵活和强大的功能,可以让我们轻松地与远程服务器进行交互。通过了解它们的源码,我们可以更好地理解 HTTP 请求的原理,并能够更加灵活地使用它们来解决实际问题。

结束语:代码的世界,探索永无止境!

希望今天的讲解能够帮助大家更好地理解 wp_remote_get() 函数的源码。记住,代码的世界是充满乐趣的,探索永无止境。下次遇到类似的问题,不妨自己动手调试一下,相信你会有更多的收获!感谢大家的聆听!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注