分析 WordPress `wp_check_for_updates()` 函数的源码:如何与 WordPress.org API 交互以检查核心、插件和主题更新。

各位观众老爷们,大家好!今天咱们来聊聊 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;
}

这个函数的主要逻辑是:

  1. 获取所有已安装的插件信息 (get_plugins())。
  2. 获取存储在 update_plugins 瞬时选项中的插件更新信息 (get_site_transient( 'update_plugins' ))。
  3. 遍历所有插件,检查是否有更新。
  4. 构建一个包含插件名称、版本、更新信息的数组,并返回。

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 的自动更新机制。 以后再看到“有新版本啦!”的提示,就能知道是谁在背后默默付出了。

咱们下期再见!

发表回复

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