分析 WordPress `wp_remote_get()` 和 `wp_remote_post()` 函数源码:它们如何封装 `WP_Http` 类并处理远程请求。

WordPress 远程请求解密:wp_remote_get()wp_remote_post() 源码“扒皮”讲座

各位观众老爷,晚上好!我是你们的老朋友,代码界的段子手。今天咱们不聊风花雪月,来点硬核的——扒一扒 WordPress 中 wp_remote_get()wp_remote_post() 这哥俩的底裤,看看它们是如何调戏 WP_Http 类,实现远程请求的。

准备好了吗?老司机要开车了,请系好安全带!

开场白:为什么需要远程请求?

在 WordPress 的世界里,你可能会遇到需要跟其他服务器“眉来眼去”的情况,比如:

  • 获取外部数据: 从第三方 API 获取天气预报、股票行情、汇率信息等等。
  • 验证用户身份: 调用外部 OAuth 服务进行用户登录验证。
  • 更新软件版本: 检查 WordPress 插件或主题是否有新版本可用。
  • 发送通知: 将网站事件通知到其他服务(比如Slack、Discord)。

这时候,就需要我们的 wp_remote_get()wp_remote_post() 闪亮登场了。它们就像 WordPress 的“网络特工”,专门负责搞定这些跨服务器的任务。

正式开始:wp_remote_get()wp_remote_post() 的“前世今生”

wp_remote_get()wp_remote_post() 并不是直接赤膊上阵发送 HTTP 请求,而是站在 WP_Http 这个巨人的肩膀上。 WP_Http 类就像一个“HTTP 请求处理工厂”,而 wp_remote_get()wp_remote_post() 则是工厂里的两道“标准流水线”。

让我们先来看看 wp_remote_get() 的真面目:

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

再来看看 wp_remote_post() 的庐山真面目:

function wp_remote_post( $url, $args = array() ) {
    return wp_remote_request( $url, wp_parse_args( $args, array( 'method' => 'POST' ) ) );
}

看到没?它们俩其实都是“套娃”,本质上都是调用了 wp_remote_request() 这个更底层的函数。 区别只在于:

  • wp_remote_get() 默认将请求方法设置为 GET
  • wp_remote_post() 默认将请求方法设置为 POST

它们都接收两个参数:

  • $url: 请求的 URL 地址。
  • $args: 一个数组,包含请求的各种参数(比如 headers, body, timeout 等)。

深入虎穴:wp_remote_request() 函数解析

wp_remote_request() 函数才是真正干活的家伙。它接收 URL 和参数,然后调用 WP_Http 类来发送请求。 让我们来拆解一下它的主要流程:

  1. 参数合并与标准化: 将传入的参数与默认参数合并,并进行一些标准化处理。

  2. 请求过滤: 允许开发者通过 pre_http_request 过滤器来短路(提前返回)请求,或者修改请求参数。

  3. WP_Http 实例创建: 根据当前环境选择合适的 HTTP 传输方式(比如 CURL、Streams、Fsockopen),并创建 WP_Http 类的实例。

  4. 发送请求: 调用 WP_Http 实例的 request() 方法来发送 HTTP 请求。

  5. 响应过滤: 允许开发者通过 http_response 过滤器来修改 HTTP 响应。

  6. 处理响应: 根据响应的状态码,判断请求是否成功,并返回响应结果。

让我们用代码来展示一下这个流程 (简化版,省略了部分错误处理和过滤):

function wp_remote_request( $url, $args = array() ) {

    // 1. 参数合并与标准化
    $args = wp_parse_args( $args, array(
        'timeout'    => 5,
        'redirection' => 5,
        'httpversion' => '1.0',
        'user-agent'  => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ),
        'reject_unsafe_urls' => true,
    ) );

    // 2. 请求过滤 (省略)
    // $pre_http_request = apply_filters( 'pre_http_request', false, $url, $args );
    // if ( false !== $pre_http_request ) {
    //  return $pre_http_request;
    // }

    // 3. WP_Http 实例创建
    $http = _wp_http_get_object();  // 获取 WP_Http 实例

    // 4. 发送请求
    $response = $http->request( $url, $args );

    // 5. 响应过滤 (省略)
    // $response = apply_filters( 'http_response', $response, $args, $url );

    // 6. 处理响应
    if ( is_wp_error( $response ) ) {
        return $response;
    }

    return $response;
}

WP_Http 类:HTTP 请求的幕后英雄

WP_Http 类才是真正干脏活累活的家伙。它负责处理底层的 HTTP 协议细节,并根据当前环境选择最合适的 HTTP 传输方式。

WP_Http 类支持多种 HTTP 传输方式,包括:

  • CURL: 使用 PHP 的 CURL 扩展。这是最常用的方式,功能强大,支持各种 HTTP 特性。
  • Streams: 使用 PHP 的 Streams API。相对 CURL 来说,功能较弱,但更轻量级。
  • Fsockopen: 使用 PHP 的 Fsockopen 函数。这是最原始的方式,需要手动处理 HTTP 协议细节。

WP_Http 类会根据以下顺序选择 HTTP 传输方式:

  1. 如果 CURL 扩展可用,则使用 CURL。
  2. 如果 Streams API 可用,则使用 Streams。
  3. 如果以上两种方式都不可用,则使用 Fsockopen。

WP_Http 类的核心方法是 request(),它接收 URL 和参数,然后发送 HTTP 请求,并返回响应结果。

让我们来看看 WP_Http::request() 方法的简化版代码:

public function request( $url, $args = array() ) {

    $args = wp_parse_args( $args );

    // 根据 URL Scheme 选择传输方式
    $ssl = ( substr( $url, 0, 8 ) == 'https://' );

    // 根据参数和环境选择合适的传输方式
    if ( isset( $args['sslverify'] ) && ! $args['sslverify'] ) {
        $transports = array( 'streams', 'curl' ); // 允许不验证 SSL 证书
    } else {
        $transports = $this->get_transports(); // 获取可用的传输方式
    }

    // 循环尝试不同的传输方式
    foreach ( $transports as $transport ) {
        $response = $this->{'request_' . $transport}( $url, $args ); // 调用具体的传输方式

        if ( ! is_wp_error( $response ) ) {
            return $response; // 成功,返回响应
        }
    }

    return new WP_Error( 'http_request_failed', __( 'All HTTP request transports failed.' ) ); // 所有方式都失败
}

三大金刚:CURL, Streams, Fsockopen

WP_Http 类通过 request_curl(), request_streams(), 和 request_fsockopen() 这三个方法来分别处理 CURL, Streams, 和 Fsockopen 这三种传输方式。

由于篇幅限制,我们只简单介绍一下 CURL 的处理方式。

WP_Http::request_curl() 方法会:

  1. 初始化 CURL 句柄。
  2. 设置 CURL 选项,比如 URL, headers, body, timeout 等。
  3. 执行 CURL 请求。
  4. 获取响应结果。
  5. 关闭 CURL 句柄。

参数详解:$args 数组的秘密

wp_remote_get(), wp_remote_post(), 和 wp_remote_request() 函数都接收一个 $args 数组,用于指定请求的各种参数。 这个 $args 数组就像一个“百宝箱”,可以控制 HTTP 请求的方方面面。

下面是一些常用的 $args 参数:

参数名 类型 描述 默认值
method string 请求方法 (GET, POST, PUT, DELETE 等)。 GET (for wp_remote_get()), POST (for wp_remote_post())
timeout int 请求超时时间 (秒)。 5
redirection int 最大重定向次数。 5
httpversion string HTTP 协议版本 (1.0 或 1.1)。 1.0
user-agent string User-Agent 字符串。 WordPress/{version}; {url}
headers array 自定义 HTTP Headers。 array()
body mixed 请求体 (用于 POST, PUT 等方法)。可以是字符串或数组。 null
cookies array 要发送的 Cookie。 array()
sslverify bool 是否验证 SSL 证书。 true (建议保持默认值,除非你知道自己在做什么)
stream bool 是否以流式传输方式获取响应。 false
filename string stream 为 true, 则指定保存响应内容的文件路径。 ''

示例:发送一个带有自定义 Header 和 Body 的 POST 请求

$url = 'https://example.com/api/endpoint';
$args = array(
    'method'  => 'POST',
    'timeout' => 10,
    'headers' => array(
        'Content-Type' => 'application/json',
        'X-API-Key'    => 'YOUR_API_KEY',
    ),
    'body'    => json_encode( array( 'name' => 'John Doe', 'email' => '[email protected]' ) ),
);

$response = wp_remote_post( $url, $args );

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

响应处理:从“乱码”到“有用信息”

wp_remote_get()wp_remote_post() 返回的响应结果是一个数组,包含以下元素:

  • headers: HTTP 响应头。
  • body: HTTP 响应体。
  • response: 一个数组,包含响应状态码和状态信息。
  • cookies: 服务器设置的 Cookie。
  • filename: 如果使用了 stream 参数,则包含保存响应内容的文件路径。

为了方便获取响应体,WordPress 提供了 wp_remote_retrieve_body() 函数:

$body = wp_remote_retrieve_body( $response );

同样,为了方便获取响应头,可以使用 wp_remote_retrieve_headers() 函数:

$headers = wp_remote_retrieve_headers( $response );

示例:获取响应状态码和响应体

$url = 'https://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 {
    $status_code = wp_remote_retrieve_response_code( $response );
    $body = wp_remote_retrieve_body( $response );

    echo "Status Code: $status_coden";
    echo "Body: $bodyn";
}

错误处理:避免“原地爆炸”

在进行远程请求时,可能会遇到各种错误,比如:

  • 网络连接失败。
  • 服务器返回错误状态码 (404, 500 等)。
  • 请求超时。
  • SSL 证书验证失败。

wp_remote_get()wp_remote_post() 在遇到错误时,会返回一个 WP_Error 对象。 因此,在处理响应结果之前,务必先检查是否发生了错误:

$response = wp_remote_get( $url );

if ( is_wp_error( $response ) ) {
    $error_message = $response->get_error_message();
    echo "Something went wrong: $error_message";
    // 进行错误处理,比如记录日志、显示错误信息等
} else {
    // 处理响应结果
}

安全提示:防范“黑客入侵”

在进行远程请求时,需要注意以下安全问题:

  • URL 验证: 对 URL 进行验证,防止恶意用户注入恶意 URL。
  • 数据过滤: 对从外部服务器获取的数据进行过滤,防止 XSS 攻击。
  • SSL 验证: 始终验证 SSL 证书,防止中间人攻击 (除非你非常清楚自己在做什么)。
  • 限制请求频率: 限制对外部服务器的请求频率,防止 DDOS 攻击。
  • 存储敏感信息: 不要将 API Key 等敏感信息直接写在代码中,而是应该存储在安全的地方 (比如 WordPress 的 Options API)。

总结:wp_remote_get()wp_remote_post() 的“葵花宝典”

wp_remote_get()wp_remote_post() 是 WordPress 中进行远程请求的利器。 它们封装了 WP_Http 类,提供了简单易用的 API。 理解它们的底层原理,可以帮助你更好地利用它们,解决各种实际问题。

核心要点:

  • wp_remote_get()wp_remote_post() 都是 wp_remote_request() 的“马甲”。
  • wp_remote_request() 负责参数处理、请求过滤、WP_Http 实例创建和响应处理。
  • WP_Http 类是 HTTP 请求的幕后英雄,负责处理底层的 HTTP 协议细节。
  • $args 数组是控制 HTTP 请求的“百宝箱”。
  • 务必进行错误处理和安全防护。

希望今天的“扒皮”讲座能让你对 WordPress 的远程请求有更深入的了解。 记住,代码的世界里没有秘密,只有不断学习和探索才能成为真正的“代码大师”。

下课!

发表回复

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