各位观众老爷们,大家好!今天咱们来聊聊 WordPress 自动更新的幕后英雄——wp_check_for_updates()
函数。 这家伙藏得挺深,但却是 WordPress 能自动告诉你“有新版本啦!快来升级!”的关键所在。
咱们的目标是抽丝剥茧,看看它是怎么跟 WordPress.org 上的 API 眉来眼去,打听核心、插件、主题有没有新情况的。准备好了吗?发车!
1. 预热:wp-cron.php
和计划任务
在深入 wp_check_for_updates()
之前,先得简单了解下 WordPress 的“定时炸弹”—— wp-cron.php
。 这家伙可不是真正的 cron 任务,而是一种模拟 cron 的方法。
WordPress 会通过 wp-cron.php
定期执行一些计划任务,比如发布定时文章、清理过期缓存等等。而检查更新,通常也是通过一个名为 wp_version_check
的计划任务来触发的。
这个 wp_version_check
任务默认每天执行两次(可以通过 wp_schedule_event()
函数自定义频率)。 每次执行,它就会调用 wp_check_for_updates()
函数。
2. wp_check_for_updates()
函数概览
wp_check_for_updates()
函数的代码位于 wp-includes/update.php
文件中。 它的主要职责是:
- 收集当前 WordPress 版本、插件、主题的信息。
- 构建一个包含这些信息的请求,发送给 WordPress.org 的 API。
- 解析 API 返回的 JSON 数据,判断是否有更新。
- 如果有更新,将更新信息存储到 WordPress 的选项中,以便在后台显示。
3. 代码拆解:一步一步看它怎么干活
咱们从代码入手,一步一步分析 wp_check_for_updates()
的核心逻辑。
function wp_check_for_updates( $hook_extra = array() ) {
global $wp_version, $wpdb;
if ( defined( 'WP_INSTALLING' ) ) {
return false;
}
// 检查是否禁用更新
if ( wp_installing() || apply_filters( 'pre_site_transient_update_core', false ) ) {
return false;
}
// 检查上次检查更新的时间,避免频繁请求
$last_checked = get_site_transient( 'update_core' );
if ( is_object( $last_checked ) && ! empty( $last_checked->last_checked ) ) {
$time_not_changed = 43200; // 12 hours
if ( time() - $last_checked->last_checked < $time_not_changed ) {
return false;
}
}
// 获取当前 WordPress 版本
include( ABSPATH . WPINC . '/version.php' );
$wp_version = isset( $wp_version ) ? $wp_version : '0';
$core = new stdClass();
$core->updates = array();
// 获取当前插件信息
$plugins = get_plugins();
$active_plugins = get_option( 'active_plugins', array() );
// 获取当前主题信息
$themes = wp_get_themes();
// 构建请求参数
$options = array(
'timeout' => ( ( defined( 'DOING_CRON' ) && DOING_CRON ) ? 30 : 3 ),
'body' => array(
'plugins' => wp_json_encode( get_plugin_updates() ),
'themes' => wp_json_encode( get_theme_updates() ),
'active' => wp_json_encode( get_option( 'active_plugins', array() ) ),
'version' => $wp_version,
'php' => phpversion(),
'locale' => get_locale(),
'mysql' => $wpdb->db_version(),
'extra' => $hook_extra,
),
'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
);
// 发送请求到 WordPress.org API
$url = $http_url = 'http://api.wordpress.org/core/version-check/1.7/';
if ( $ssl = wp_http_supports( array( 'ssl' => true ) ) ) {
$url = set_url_scheme( $url, 'https' );
}
$raw_response = wp_remote_post( $url, $options );
if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
return false;
}
// 解析 API 返回的数据
$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
if ( is_array( $response ) ) {
// 处理核心更新
if ( isset( $response['core'] ) ) {
$core->updates = $response['core'];
set_site_transient( 'update_core', $core, 3 * HOUR_IN_SECONDS );
}
// 处理插件更新
if ( isset( $response['plugins'] ) ) {
set_site_transient( 'update_plugins', $response['plugins'], 3 * HOUR_IN_SECONDS );
}
// 处理主题更新
if ( isset( $response['themes'] ) ) {
set_site_transient( 'update_themes', $response['themes'], 3 * HOUR_IN_SECONDS );
}
do_action( '_after_core_updated', $core->updates );
}
return true;
}
现在,咱们把这段代码掰开了揉碎了,逐行分析:
3.1. 前置检查
if ( defined( 'WP_INSTALLING' ) ) {
return false;
}
// 检查是否禁用更新
if ( wp_installing() || apply_filters( 'pre_site_transient_update_core', false ) ) {
return false;
}
这两段代码主要做的是“安全检查”。
defined( 'WP_INSTALLING' )
:如果 WordPress 正在安装,就跳过更新检查,避免干扰安装过程。wp_installing()
:如果 WordPress 正在升级(升级过程也可能用到安装状态),也跳过。apply_filters( 'pre_site_transient_update_core', false )
:允许通过pre_site_transient_update_core
过滤器禁用核心更新检查。这个过滤器可以返回一个非false
的值,如果返回了,就表示禁用了更新检查。
3.2. 频率限制
$last_checked = get_site_transient( 'update_core' );
if ( is_object( $last_checked ) && ! empty( $last_checked->last_checked ) ) {
$time_not_changed = 43200; // 12 hours
if ( time() - $last_checked->last_checked < $time_not_changed ) {
return false;
}
}
这段代码的作用是限制更新检查的频率。 避免 WordPress 频繁地向 WordPress.org 发送请求,造成服务器压力。
get_site_transient( 'update_core' )
:从数据库中获取名为update_core
的瞬时选项(transient option)。这个选项存储了上次检查更新的时间。$time_not_changed = 43200; // 12 hours
:定义了最小检查间隔,这里是 12 小时。time() - $last_checked->last_checked < $time_not_changed
:如果距离上次检查更新的时间小于 12 小时,就跳过本次检查。
3.3. 收集信息
include( ABSPATH . WPINC . '/version.php' );
$wp_version = isset( $wp_version ) ? $wp_version : '0';
$core = new stdClass();
$core->updates = array();
// 获取当前插件信息
$plugins = get_plugins();
$active_plugins = get_option( 'active_plugins', array() );
// 获取当前主题信息
$themes = wp_get_themes();
这里开始收集需要发送给 WordPress.org 的信息。
include( ABSPATH . WPINC . '/version.php' )
:引入wp-includes/version.php
文件,这个文件定义了$wp_version
变量,表示当前 WordPress 的版本。get_plugins()
:获取所有已安装的插件信息。get_option( 'active_plugins', array() )
:获取已激活的插件列表。wp_get_themes()
:获取所有已安装的主题信息。
3.4. 构建请求参数
$options = array(
'timeout' => ( ( defined( 'DOING_CRON' ) && DOING_CRON ) ? 30 : 3 ),
'body' => array(
'plugins' => wp_json_encode( get_plugin_updates() ),
'themes' => wp_json_encode( get_theme_updates() ),
'active' => wp_json_encode( get_option( 'active_plugins', array() ) ),
'version' => $wp_version,
'php' => phpversion(),
'locale' => get_locale(),
'mysql' => $wpdb->db_version(),
'extra' => $hook_extra,
),
'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
);
这段代码构建了发送给 WordPress.org API 的请求参数。
timeout
:设置请求超时时间。如果在 cron 任务中执行,超时时间设置为 30 秒,否则设置为 3 秒。body
:请求的主体,包含以下信息:plugins
:所有插件的更新信息,通过get_plugin_updates()
函数获取。themes
:所有主题的更新信息,通过get_theme_updates()
函数获取。active
:已激活的插件列表。version
:当前 WordPress 版本。php
:当前 PHP 版本。locale
:当前 WordPress 语言环境。mysql
:当前 MySQL 版本。extra
:额外的参数,可以通过wp_check_for_updates
函数的$hook_extra
参数传入。
user-agent
:设置 User-Agent 头部,用于标识请求的来源。
3.5. 发送请求
$url = $http_url = 'http://api.wordpress.org/core/version-check/1.7/';
if ( $ssl = wp_http_supports( array( 'ssl' => true ) ) ) {
$url = set_url_scheme( $url, 'https' );
}
$raw_response = wp_remote_post( $url, $options );
这段代码发送 HTTP 请求到 WordPress.org API。
$url
:API 的 URL,默认为http://api.wordpress.org/core/version-check/1.7/
。wp_http_supports( array( 'ssl' => true ) )
:检查服务器是否支持 SSL。如果支持,则将 URL 修改为 HTTPS。wp_remote_post( $url, $options )
:使用 WordPress 内置的 HTTP API 发送 POST 请求。
3.6. 处理响应
if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
return false;
}
// 解析 API 返回的数据
$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
if ( is_array( $response ) ) {
// 处理核心更新
if ( isset( $response['core'] ) ) {
$core->updates = $response['core'];
set_site_transient( 'update_core', $core, 3 * HOUR_IN_SECONDS );
}
// 处理插件更新
if ( isset( $response['plugins'] ) ) {
set_site_transient( 'update_plugins', $response['plugins'], 3 * HOUR_IN_SECONDS );
}
// 处理主题更新
if ( isset( $response['themes'] ) ) {
set_site_transient( 'update_themes', $response['themes'], 3 * HOUR_IN_SECONDS );
}
do_action( '_after_core_updated', $core->updates );
}
这段代码处理 WordPress.org API 返回的响应。
is_wp_error( $raw_response )
:检查请求是否发生错误。200 != wp_remote_retrieve_response_code( $raw_response )
:检查 HTTP 响应码是否为 200 (OK)。json_decode( wp_remote_retrieve_body( $raw_response ), true )
:将 API 返回的 JSON 数据解析为 PHP 数组。$response['core']
:如果 API 返回了核心更新信息,则将其存储到update_core
瞬时选项中。$response['plugins']
:如果 API 返回了插件更新信息,则将其存储到update_plugins
瞬时选项中。$response['themes']
:如果 API 返回了主题更新信息,则将其存储到update_themes
瞬时选项中。set_site_transient( ... , 3 * HOUR_IN_SECONDS )
:将更新信息存储到瞬时选项中,有效期为 3 小时。do_action( '_after_core_updated', $core->updates )
:触发_after_core_updated
动作,允许其他插件或主题在核心更新信息更新后执行一些操作。
4. 插件和主题更新信息:get_plugin_updates()
和 get_theme_updates()
刚才我们提到了 get_plugin_updates()
和 get_theme_updates()
这两个函数,它们分别用于获取插件和主题的更新信息。 咱们也简单看看它们的实现。
4.1. get_plugin_updates()
function get_plugin_updates() {
global $wp_version;
$plugins = get_plugins();
$plugin_updates = get_site_transient( 'update_plugins' );
$updates = array();
$stylesheet = wp_normalize_path( WP_PLUGIN_DIR . '/' );
foreach ( $plugins as $plugin_file => $plugin_data ) {
// Normalize plugin file
$plugin_file = wp_normalize_path( WP_PLUGIN_DIR . '/' . $plugin_file );
$update = false;
if ( isset( $plugin_updates->response[ $plugin_file ] ) ) {
$update = $plugin_updates->response[ $plugin_file ];
} elseif ( isset( $plugin_updates->no_update[ $plugin_file ] ) ) {
$update = false;
} else {
$update = false;
}
$version = isset( $plugin_data['Version'] ) ? $plugin_data['Version'] : '';
$updates[ $plugin_file ] = (object) array(
'Name' => $plugin_data['Name'],
'PluginURI' => $plugin_data['PluginURI'],
'Version' => $version,
'Update' => $update,
);
}
return $updates;
}
这个函数的主要逻辑是:
- 获取所有已安装的插件信息 (
get_plugins()
)。 - 获取存储在
update_plugins
瞬时选项中的插件更新信息 (get_site_transient( 'update_plugins' )
)。 - 遍历所有插件,检查是否有更新。
- 构建一个包含插件名称、版本、更新信息的数组,并返回。
4.2. get_theme_updates()
function get_theme_updates() {
$themes = wp_get_themes();
$theme_updates = get_site_transient( 'update_themes' );
$updates = array();
foreach ( $themes as $stylesheet => $theme ) {
$update = false;
if ( isset( $theme_updates->response[ $stylesheet ] ) ) {
$update = $theme_updates->response[ $stylesheet ];
} elseif ( isset( $theme_updates->no_update[ $stylesheet ] ) ) {
$update = false;
} else {
$update = false;
}
$version = $theme->get( 'Version' );
$updates[ $stylesheet ] = (object) array(
'Name' => $theme->get( 'Name' ),
'ThemeURI' => $theme->get( 'ThemeURI' ),
'Version' => $version,
'Update' => $update,
);
}
return $updates;
}
get_theme_updates()
函数的逻辑与 get_plugin_updates()
类似,只不过是针对主题进行操作。
5. WordPress.org API 返回的数据格式
WordPress.org API 返回的 JSON 数据格式大致如下:
{
"core": [
{
"response": "upgrade",
"download": "https://wordpress.org/latest.zip",
"locale": "zh_CN",
"packages": {
"full": "https://wordpress.org/latest.zip",
"no_content": "https://wordpress.org/latest.zip"
},
"current": "5.8.2",
"version": "5.9",
"php_version": "5.6.20",
"mysql_version": "5.0",
"new_files": true,
"security": false
}
],
"plugins": {
"akismet/akismet.php": {
"slug": "akismet",
"new_version": "4.2.2",
"url": "https://wordpress.org/plugins/akismet/",
"package": "https://downloads.wordpress.org/plugin/akismet.4.2.2.zip",
"icons": {
"1x": "https://ps.w.org/akismet/assets/icon-128x128.png",
"2x": "https://ps.w.org/akismet/assets/icon-256x256.png"
},
"banners": {
"low": "https://ps.w.org/akismet/assets/banner-772x250.png",
"high": "https://ps.w.org/akismet/assets/banner-1544x500.png"
},
"banners_rtl": {},
"tested": "5.8.2",
"requires_php": "5.3"
}
},
"themes": {
"twentytwentyone": {
"new_version": "1.4",
"url": "https://wordpress.org/themes/twentytwentyone/",
"package": "https://downloads.wordpress.org/theme/twentytwentyone.1.4.zip",
"icons": {
"1x": "https://s.w.org/style/images/wp-header-logo-83x89.png",
"2x": "https://s.w.org/style/images/wp-header-logo-2x.png"
},
"banners": {},
"banners_rtl": {},
"tested": "5.8.2",
"requires_php": "5.6"
}
}
}
这个 JSON 数据包含了核心、插件、主题的更新信息。 具体的字段含义可以参考 WordPress 官方文档。
6. 总结
好了,各位观众老爷们,今天咱们一起深入分析了 WordPress wp_check_for_updates()
函数的源码,了解了它是如何与 WordPress.org API 交互,检查核心、插件和主题更新的。
简单总结一下:
wp_check_for_updates()
函数通过wp-cron.php
定期执行。- 它会收集 WordPress 版本、插件、主题的信息,构建请求参数,发送给 WordPress.org API。
- API 返回 JSON 数据,包含核心、插件、主题的更新信息。
wp_check_for_updates()
函数解析 JSON 数据,将更新信息存储到 WordPress 的选项中。
希望今天的讲解能帮助大家更好地理解 WordPress 的自动更新机制。 以后再看到“有新版本啦!”的提示,就能知道是谁在背后默默付出了。
咱们下期再见!