各位观众老爷们,大家好!今天咱们来聊聊 WordPress 里一个非常低调但又至关重要的函数:wp_remote_get()
。它就像个默默无闻的信使,穿梭在你的 WordPress 站点和遥远的服务器之间,帮你取回各种数据。
别看它名字简单,wp_remote_get()
实际上是 WordPress HTTP API 的冰山一角。它背后隐藏着一个强大的类:WP_Http
。今天,咱们就一层层扒开它的源码,看看这个信使是怎么工作的,以及它是如何巧妙地封装 WP_Http
类来处理 HTTP 请求的。
wp_remote_get()
:一个友好的包装
首先,让我们来看看 wp_remote_get()
函数本身的代码(简化版,去掉了各种过滤器和错误处理,重点突出核心逻辑):
function wp_remote_get( $url, $args = array() ) {
$http = _wp_http_get_object(); // 获取 WP_Http 实例
return $http->get( $url, $args ); // 调用 WP_Http 对象的 get 方法
}
function _wp_http_get_object() {
static $http;
if ( is_null( $http ) ) {
$http = new WP_Http();
}
return $http;
}
简单来说,wp_remote_get()
干了两件事:
-
获取
WP_Http
对象: 它通过_wp_http_get_object()
函数获取一个WP_Http
类的实例。这里用到了一个静态变量$http
,保证了在同一个请求周期内,只会创建一个WP_Http
对象,节省资源。 这也是一种单例模式的简单实现。 -
调用
get()
方法: 它直接调用WP_Http
对象的get()
方法,并将 URL 和参数传递给它。
所以,真正的英雄是 WP_Http
类。wp_remote_get()
只是给它穿了一层友好的外衣,方便我们使用。
WP_Http
类:幕后英雄登场
WP_Http
类才是真正干活的。它负责建立连接、发送请求、接收响应,以及处理各种 HTTP 协议的细节。让我们深入 WP_Http
类,看看它的 get()
方法是如何工作的。
class WP_Http {
public function get( $url, $args = array() ) {
return $this->request( $url, wp_parse_args( $args, array( 'method' => 'GET' ) ) );
}
public function request( $url, $args = array() ) {
$args = wp_parse_args( $args );
// 一大堆参数处理、过滤器... 这里省略
$transport = $this->_get_first_available_transport( $url, $args );
if ( ! $transport ) {
return new WP_Error( 'http_no_url', __( 'Invalid URL provided.' ) );
}
$response = $transport->request( $url, $args );
// 一大堆响应处理、过滤器... 这里省略
return $response;
}
private function _get_first_available_transport( $url, $args = array() ) {
$transports = array(
'curl' => 'WP_Http_Curl',
'streams' => 'WP_Http_Streams',
'fsockopen' => 'WP_Http_Fsockopen',
);
// 根据环境判断依次使用什么方法
foreach ( $transports as $key => $class ) {
$transport = new $class();
if ( $transport->test( $url, $args ) ) {
return $transport;
}
}
return false;
}
}
WP_Http
类的 get()
方法,也没干什么特别的事情,它只是调用了 request()
方法,并设置了请求方法为 "GET"。真正的核心逻辑都在 request()
方法里。
request()
方法做了几件事:
-
参数解析:
wp_parse_args()
函数将传入的参数和默认参数合并,确保参数的完整性。 -
选择传输方式: 这是关键的一步。
_get_first_available_transport()
方法会根据当前服务器环境,选择一个可用的 HTTP 传输方式。常见的传输方式有:- cURL: 如果服务器安装了 cURL 扩展,
WP_Http_Curl
类会被优先选择。cURL 是一个功能强大的 HTTP 客户端,支持各种协议和选项。 - Streams: 如果 cURL 不可用,
WP_Http_Streams
类会尝试使用 PHP 的 Streams API。Streams API 提供了一种更底层的 HTTP 通信方式。 - fsockopen: 如果 Streams API 也不可用,
WP_Http_Fsockopen
类会使用fsockopen()
函数直接建立 socket 连接。这是最底层的 HTTP 通信方式。
_get_first_available_transport()
会依次尝试这些传输方式,直到找到一个可用的为止。test()
方法用于判断当前传输方式是否可用。 - cURL: 如果服务器安装了 cURL 扩展,
-
发送请求: 一旦选择了传输方式,
request()
方法会调用对应传输类的request()
方法,发送 HTTP 请求。 -
处理响应: 接收到 HTTP 响应后,
request()
方法会对响应进行处理,例如解析 HTTP 头、提取响应体等。
HTTP 传输类:各显神通
现在,让我们深入了解一下 WP_Http
类使用的三种 HTTP 传输类:WP_Http_Curl
、WP_Http_Streams
和 WP_Http_Fsockopen
。
WP_Http_Curl
类:cURL 大法好
如果服务器安装了 cURL 扩展,WP_Http_Curl
类通常是首选。cURL 是一个功能强大的 HTTP 客户端,支持各种协议和选项。
class WP_Http_Curl {
public function test( $url = '', $args = array() ) {
if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) {
return false;
}
return true;
}
public function request( $url, $args = array() ) {
$ch = curl_init();
// 设置 cURL 选项
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); // 返回结果,不直接输出
curl_setopt( $ch, CURLOPT_HEADER, true ); // 返回 header
// ... 其他 cURL 选项 ...
// 发送请求
$response = curl_exec( $ch );
// 处理响应
$header_size = curl_getinfo( $ch, CURLINFO_HEADER_SIZE );
$header = substr( $response, 0, $header_size );
$body = substr( $response, $header_size );
// ... 整理响应数据 ...
curl_close( $ch );
return $response_array;
}
}
WP_Http_Curl
类的 request()
方法主要做了以下几件事:
-
初始化 cURL:
curl_init()
函数初始化一个 cURL 会话。 -
设置 cURL 选项:
curl_setopt()
函数用于设置 cURL 选项,例如:CURLOPT_URL
:设置请求的 URL。CURLOPT_RETURNTRANSFER
:设置是否返回结果,而不是直接输出。CURLOPT_HEADER
:设置是否返回 HTTP 头。CURLOPT_POSTFIELDS
:设置 POST 请求的数据。CURLOPT_HTTPHEADER
:设置自定义 HTTP 头。CURLOPT_TIMEOUT
:设置超时时间。
-
发送请求:
curl_exec()
函数发送 HTTP 请求,并返回响应结果。 -
处理响应:
curl_getinfo()
函数获取 cURL 会话的信息,例如 HTTP 状态码、响应头大小等。然后,将响应结果分割成 HTTP 头和响应体。 -
关闭 cURL 会话:
curl_close()
函数关闭 cURL 会话,释放资源。
WP_Http_Streams
类:Streams API 的优雅
如果 cURL 不可用,WP_Http_Streams
类会尝试使用 PHP 的 Streams API。Streams API 提供了一种更底层的 HTTP 通信方式,但比直接使用 socket 连接更方便。
class WP_Http_Streams {
public function test( $url = '', $args = array() ) {
return function_exists( 'stream_socket_client' );
}
public function request( $url, $args = array() ) {
// 构建 HTTP 请求头
$request_header = '';
// 构建 HTTP 请求体
$request_body = '';
// 创建 stream 上下文
$context = stream_context_create( array(
'http' => array(
'method' => $args['method'],
'header' => $request_header,
'content' => $request_body,
'timeout' => $args['timeout'],
),
) );
// 发送请求
$stream = @fopen( $url, 'r', false, $context );
// 处理响应
$response_header = stream_get_meta_data( $stream );
$response_body = stream_get_contents( $stream );
// ... 整理响应数据 ...
fclose( $stream );
return $response_array;
}
}
WP_Http_Streams
类的 request()
方法主要做了以下几件事:
-
构建 HTTP 请求头和请求体: 根据传入的参数,构建 HTTP 请求头和请求体。
-
创建 stream 上下文:
stream_context_create()
函数创建一个 stream 上下文,用于配置 HTTP 请求的各种选项,例如请求方法、HTTP 头、请求体、超时时间等。 -
发送请求:
fopen()
函数使用 stream 上下文打开一个 URL,相当于发送 HTTP 请求。 -
处理响应:
stream_get_meta_data()
函数获取 stream 的元数据,包括 HTTP 头。stream_get_contents()
函数获取 stream 的内容,即响应体。 -
关闭 stream:
fclose()
函数关闭 stream,释放资源。
WP_Http_Fsockopen
类:Socket 连接的原始力量
如果 cURL 和 Streams API 都不可用,WP_Http_Fsockopen
类会使用 fsockopen()
函数直接建立 socket 连接。这是最底层的 HTTP 通信方式,需要手动处理 HTTP 协议的细节。
class WP_Http_Fsockopen {
public function test( $url = '', $args = array() ) {
return function_exists( 'fsockopen' );
}
public function request( $url, $args = array() ) {
// 解析 URL
$parsed_url = parse_url( $url );
// 建立 socket 连接
$host = $parsed_url['host'];
$port = isset( $parsed_url['port'] ) ? $parsed_url['port'] : 80;
$fp = fsockopen( $host, $port, $errno, $errstr, $args['timeout'] );
// 构建 HTTP 请求
$request = '';
// 发送 HTTP 请求
fwrite( $fp, $request );
// 接收响应
$response = '';
while ( ! feof( $fp ) ) {
$response .= fgets( $fp, 1160 );
}
// 处理响应
// ... 整理响应数据 ...
fclose( $fp );
return $response_array;
}
}
WP_Http_Fsockopen
类的 request()
方法主要做了以下几件事:
-
解析 URL:
parse_url()
函数解析 URL,获取主机名、端口号等信息。 -
建立 socket 连接:
fsockopen()
函数建立一个 socket 连接到指定的服务器。 -
构建 HTTP 请求: 手动构建 HTTP 请求,包括请求行、HTTP 头和请求体。
-
发送 HTTP 请求:
fwrite()
函数将 HTTP 请求发送到服务器。 -
接收响应:
fgets()
函数从 socket 连接中读取数据,直到读取完整个 HTTP 响应。 -
处理响应: 手动解析 HTTP 响应,包括 HTTP 状态码、HTTP 头和响应体。
-
关闭 socket 连接:
fclose()
函数关闭 socket 连接,释放资源。
总结
现在,让我们回顾一下 wp_remote_get()
函数是如何封装 WP_Http
类并处理 HTTP 请求的:
步骤 | 函数/类 | 描述 |
---|---|---|
1 | wp_remote_get() |
作为入口函数,接收 URL 和参数,并调用 WP_Http 类的 get() 方法。 |
2 | WP_Http::get() |
调用 WP_Http::request() 方法,并设置请求方法为 "GET"。 |
3 | WP_Http::request() |
解析参数,选择合适的 HTTP 传输方式(cURL、Streams API 或 socket 连接),并调用对应传输类的 request() 方法。 |
4 | WP_Http_Curl::request() / WP_Http_Streams::request() / WP_Http_Fsockopen::request() |
根据选择的传输方式,发送 HTTP 请求,接收 HTTP 响应,并处理响应数据。 |
wp_remote_get()
函数的设计充分考虑了服务器环境的兼容性。它会根据服务器是否安装了 cURL 扩展,以及是否支持 Streams API,自动选择合适的 HTTP 传输方式。这使得 WordPress 能够在各种服务器环境下正常工作。
wp_remote_get()
只是 WordPress HTTP API 的一个简单例子。WP_Http
类还提供了其他方法,例如 post()
、head()
、put()
、delete()
等,用于发送不同类型的 HTTP 请求。此外,WP_Http
类还提供了丰富的选项,用于配置 HTTP 请求的各种参数,例如超时时间、HTTP 头、代理服务器等。
掌握 wp_remote_get()
函数和 WP_Http
类的原理,可以帮助你更好地理解 WordPress 的 HTTP API,并在开发 WordPress 插件和主题时,更灵活地处理 HTTP 请求。
好了,今天的讲座就到这里。希望大家有所收获!下次再见!