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

同学们,老司机我今天就来给你们扒一扒 WordPress 升级检查的核心函数 wp_check_for_updates() 的底裤!别怕,这玩意儿虽然看起来复杂,但其实就像组装乐高一样,只要你理解了每个模块的作用,就能轻松驾驭。

开场白:更新的重要性,就像吃饭一样重要!

想象一下,你的 WordPress 网站就像一辆汽车,核心、插件和主题就是汽车的各个部件。如果这些部件长时间不保养、不升级,就会出现各种问题,比如安全漏洞、性能下降,甚至直接趴窝!所以,定期检查并更新你的 WordPress 网站至关重要,就像每天吃饭一样重要!

wp_check_for_updates() 函数,就是那个负责告诉你:“嘿,你的车该保养了,有新的零件可以换了!” 的家伙。

第一部分:wp_check_for_updates() 函数的入口

首先,我们来看看 wp_check_for_updates() 函数在哪儿以及它主要做了些什么:

// wp-includes/update.php

function wp_check_for_updates( $args = array() ) {
    global $wpdb, $wp_version, $pagenow;

    if ( defined( 'WP_INSTALLING' ) ) {
        return false;
    }

    // 确保我们拥有更新 API
    if ( ! function_exists( 'plugins_api' ) ) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }

    $defaults = array(
        'suppress_plugins' => false,
        'suppress_themes'  => false,
        'force_check'      => false,
    );
    $args     = wp_parse_args( $args, $defaults );

    // 检查是否应该运行更新检查
    if ( ! $args['force_check'] && ! should_check_for_updates() ) {
        return false;
    }

    // 阻止在特定页面上运行更新检查,例如插件/主题编辑器
    if ( in_array( $pagenow, array( 'plugin-editor.php', 'theme-editor.php' ), true ) ) {
        return false;
    }

    // 核心更新检查
    wp_version_check();

    // 插件更新检查
    if ( ! $args['suppress_plugins'] ) {
        wp_update_plugins();
    }

    // 主题更新检查
    if ( ! $args['suppress_themes'] ) {
        wp_update_themes();
    }

    // 缓存清除
    do_action( 'wp_maybe_delete_old_core_updates' );

    return true;
}

简单来说,这个函数做了以下几件事:

  1. 各种检查: 检查是否正在安装 WordPress,是否定义了 WP_INSTALLING 常量,是否应该进行更新检查,以及当前页面是否是插件或主题编辑器页面(在这些页面上检查更新可能会导致问题)。
  2. 核心更新检查: 调用 wp_version_check() 函数来检查 WordPress 核心是否有更新。
  3. 插件更新检查: 如果 $args['suppress_plugins']false,则调用 wp_update_plugins() 函数来检查插件是否有更新。
  4. 主题更新检查: 如果 $args['suppress_themes']false,则调用 wp_update_themes() 函数来检查主题是否有更新。
  5. 缓存清除: 执行 wp_maybe_delete_old_core_updates 动作,用于清除旧的核心更新缓存。

第二部分:核心更新检查 wp_version_check()

核心更新的检查是整个更新流程的基石。让我们深入了解 wp_version_check() 函数:

// wp-includes/update.php

function wp_version_check( $deprecated = '' ) {
    global $wp_version, $wp_db_version, $pagenow;

    if ( ! empty( $deprecated ) ) {
        _deprecated_argument( __FUNCTION__, '3.8' );
    }

    // 检查是否应该运行更新检查
    if ( ! should_check_for_updates() ) {
        return false;
    }

    $timeout = ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) ? 5 : 3;
    $options = array(
        'timeout' => $timeout,
    );

    $response = wp_remote_post( 'https://api.wordpress.org/core/version-check/1.7/', array(
        'body'       => array(
            'version' => $wp_version,
            'php'     => phpversion(),
            'locale'  => get_locale(),
            'mysql'   => $wpdb->db_version(),
            'blogs'   => count( get_sites() ),
            'users'   => $wpdb->get_var( "SELECT COUNT(ID) FROM $wpdb->users" ),
            'multisite' => is_multisite(),
            'sslverify' => wp_http_validate_ssl_cert(),
        ),
        'user-agent' => 'WordPress/' . $wp_version . '; ' . get_site_url()
    ), $options );

    if ( is_wp_error( $response ) ) {
        return false;
    }

    $body = trim( wp_remote_retrieve_body( $response ) );

    if ( $body ) {
        $decoded_response = json_decode( $body, true );

        if ( is_array( $decoded_response ) ) {
            $update_data = array();
            foreach ( $decoded_response['offers'] as $offer ) {
                $update_data[ $offer['response'] ] = $offer;
            }

            if ( ! empty( $update_data ) ) {
                set_site_transient( 'update_core', (object) $update_data, 3600 * 12 );
            }
        }
    }

    do_action( '_after_core_updated', $response );
}

这个函数的主要步骤如下:

  1. 准备数据: 收集当前 WordPress 版本、PHP 版本、数据库版本、站点数量、用户数量等信息,这些信息将作为请求体发送到 WordPress.org API。
  2. 发起 HTTP 请求: 使用 wp_remote_post() 函数向 https://api.wordpress.org/core/version-check/1.7/ 发送 POST 请求。
  3. 处理响应: 如果请求成功,则解析 JSON 响应。响应中包含有关可用更新的信息。
  4. 存储更新信息: 将解析后的更新信息存储在 update_core 瞬态(transient)中,以便后续使用。瞬态就像一个临时的缓存,可以设置过期时间。

HTTP 请求的细节:

  • URL: https://api.wordpress.org/core/version-check/1.7/ 这是 WordPress 官方提供的 API 端点,专门用于检查核心更新。
  • 方法: POST 使用 POST 方法发送数据,将当前 WordPress 站点的相关信息发送给 API。
  • 请求体 (body): 一个关联数组,包含以下键值对:
    • version: 当前 WordPress 版本。
    • php: 当前 PHP 版本。
    • locale: 当前站点语言环境。
    • mysql: 当前 MySQL 数据库版本。
    • blogs: 站点数量 (仅在多站点模式下有意义)。
    • users: 用户数量。
    • multisite: 是否为多站点。
    • sslverify: 是否验证 SSL 证书。
  • User-Agent: 一个字符串,包含 WordPress 版本和站点 URL,用于标识请求的来源。

JSON 响应的格式:

WordPress.org API 返回的 JSON 响应是一个包含 offers 键的数组。 offers 数组包含有关可用更新的详细信息。例如:

{
  "offers": [
    {
      "response": "upgrade",
      "download": "https://downloads.wordpress.org/release/wordpress-6.4.3.zip",
      "locale": "zh_CN",
      "packages": {
        "full": "https://downloads.wordpress.org/release/wordpress-6.4.3-zh_CN.zip",
        "no_content": "https://downloads.wordpress.org/release/wordpress-6.4.3-no-content-zh_CN.zip",
        "new_bundled": "https://downloads.wordpress.org/release/wordpress-6.4.3-new-bundled-zh_CN.zip"
      },
      "current": "6.4.2",
      "version": "6.4.3",
      "php_version": "7.0",
      "mysql_version": "5.0",
      "new_files": "5.0",
      "package": "https://downloads.wordpress.org/release/wordpress-6.4.3-zh_CN.zip",
      "md5": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "sha1": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  ]
}

其中,response 字段表示更新类型(例如,upgrade 表示需要升级)。其他字段包含有关下载链接、版本号、PHP 和 MySQL 版本要求等信息。

should_check_for_updates() 函数:

这个函数决定是否应该进行更新检查。它基于一些配置和时间间隔,以避免频繁地向 WordPress.org API 发送请求。

// wp-includes/update.php

function should_check_for_updates() {
    $time_not_checked = get_site_transient( 'update_core' );
    if ( isset( $time_not_checked->last_checked ) && ( time() - $time_not_checked->last_checked < 3600 ) ) {
        return false;
    }
    return true;
}

默认情况下,每小时检查一次核心更新。这个时间间隔可以通过过滤器进行修改。

第三部分:插件更新检查 wp_update_plugins()

插件更新的检查稍微复杂一些,因为它需要检查每个已安装的插件。

// wp-includes/update.php

function wp_update_plugins() {
    global $wp_version;

    $plugins = get_plugins();
    $active  = get_option( 'active_plugins', array() );
    $current = get_site_transient( 'update_plugins' );

    if ( ! is_array( $plugins ) ) {
        return false;
    }

    $to_send = compact( 'plugins', 'active', 'wp_version' );

    $options = array(
        'timeout' => ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) ? 5 : 3,
    );

    $response = wp_remote_post( admin_url( 'plugin-install.php' ), array(
        'body'       => array(
            'action'  => 'update-check',
            'plugins' => wp_json_encode( $to_send ),
        ),
        'user-agent' => 'WordPress/' . $wp_version . '; ' . get_site_url()
    ), $options );

    if ( is_wp_error( $response ) ) {
        return false;
    }

    $plugins = maybe_unserialize( wp_remote_retrieve_body( $response ) );

    if ( is_object( $plugins ) ) {
        set_site_transient( 'update_plugins', $plugins, 3600 * 12 );
    } elseif ( $plugins ) {
        set_site_transient( 'update_plugins', new stdClass, 3600 * 12 );
    }

    do_action( '_after_plugin_updated', $response );
}

这个函数的主要步骤如下:

  1. 获取插件信息: 使用 get_plugins() 函数获取所有已安装的插件信息。使用 get_option( 'active_plugins' ) 获取所有激活的插件信息。
  2. 准备数据: 将所有插件信息、激活插件信息和 WordPress 版本打包成一个数组 $to_send
  3. 发起 HTTP 请求: 使用 wp_remote_post() 函数向 admin_url( 'plugin-install.php' ) 发送 POST 请求。 注意,这里发送请求到 WordPress 站点自身的 plugin-install.php 文件,并设置 action 参数为 update-check
  4. 处理响应: 如果请求成功,则反序列化响应内容。响应中包含有关可用插件更新的信息。
  5. 存储更新信息: 将解析后的更新信息存储在 update_plugins 瞬态中。

HTTP 请求的细节:

  • URL: admin_url( 'plugin-install.php' ) 指向 WordPress 站点自身的 plugin-install.php 文件。
  • 方法: POST
  • 请求体 (body): 一个关联数组,包含以下键值对:
    • action: update-check 指示 plugin-install.php 执行更新检查操作。
    • plugins: 使用 wp_json_encode() 函数将 $to_send 数组编码为 JSON 字符串。
  • User-Agent: 同核心更新检查。

JSON 响应的格式:

plugin-install.php 返回的响应是一个序列化的 PHP 对象,其中包含有关可用插件更新的信息。 这个对象通常包含以下属性:

  • last_checked: 上次检查更新的时间戳。
  • checked: 一个关联数组,其中键是插件的文件路径(例如,my-plugin/my-plugin.php),值是插件的当前版本。
  • response: 一个关联数组,其中键是插件的文件路径,值是包含更新信息的对象。 如果没有更新,则该插件的文件路径将不会出现在这个数组中。

第四部分:主题更新检查 wp_update_themes()

主题更新的检查与插件更新的检查类似,只是操作对象变成了主题。

// wp-includes/update.php

function wp_update_themes() {
    global $wp_version;

    $themes = wp_get_themes();
    $current = get_site_transient( 'update_themes' );

    $options = array(
        'timeout' => ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) ? 5 : 3,
    );

    $to_send = array(
        'themes'    => array(),
        'wp_version' => $wp_version,
    );

    foreach ( $themes as $theme ) {
        $to_send['themes'][ $theme->get_stylesheet() ] = array(
            'Version' => $theme->get( 'Version' ),
            'Name'    => $theme->get( 'Name' ),
            'Author'  => $theme->get( 'Author' )
        );
    }

    $response = wp_remote_post( admin_url( 'theme-install.php' ), array(
        'body'       => array(
            'action' => 'update-check',
            'themes' => wp_json_encode( $to_send ),
        ),
        'user-agent' => 'WordPress/' . $wp_version . '; ' . get_site_url()
    ), $options );

    if ( is_wp_error( $response ) ) {
        return false;
    }

    $themes = maybe_unserialize( wp_remote_retrieve_body( $response ) );

    if ( is_object( $themes ) ) {
        set_site_transient( 'update_themes', $themes, 3600 * 12 );
    } elseif ( $themes ) {
        set_site_transient( 'update_themes', new stdClass, 3600 * 12 );
    }

    do_action( '_after_theme_updated', $response );
}

这个函数的主要步骤如下:

  1. 获取主题信息: 使用 wp_get_themes() 函数获取所有已安装的主题信息。
  2. 准备数据: 将所有主题信息和 WordPress 版本打包成一个数组 $to_send
  3. 发起 HTTP 请求: 使用 wp_remote_post() 函数向 admin_url( 'theme-install.php' ) 发送 POST 请求。 注意,这里发送请求到 WordPress 站点自身的 theme-install.php 文件,并设置 action 参数为 update-check
  4. 处理响应: 如果请求成功,则反序列化响应内容。响应中包含有关可用主题更新的信息。
  5. 存储更新信息: 将解析后的更新信息存储在 update_themes 瞬态中。

HTTP 请求的细节:

  • URL: admin_url( 'theme-install.php' ) 指向 WordPress 站点自身的 theme-install.php 文件。
  • 方法: POST
  • 请求体 (body): 一个关联数组,包含以下键值对:
    • action: update-check 指示 theme-install.php 执行更新检查操作。
    • themes: 使用 wp_json_encode() 函数将 $to_send 数组编码为 JSON 字符串。
  • User-Agent: 同核心更新检查。

JSON 响应的格式:

theme-install.php 返回的响应是一个序列化的 PHP 对象,其中包含有关可用主题更新的信息。 这个对象通常包含以下属性:

  • last_checked: 上次检查更新的时间戳。
  • checked: 一个关联数组,其中键是主题的样式表目录名(例如,twentytwentyfour),值是主题的当前版本。
  • response: 一个关联数组,其中键是主题的样式表目录名,值是包含更新信息的对象。 如果没有更新,则该主题的样式表目录名将不会出现在这个数组中。

第五部分:总结与思考

总的来说,wp_check_for_updates() 函数通过以下步骤来检查更新:

  1. 准备: 检查各种前提条件,确保可以进行更新检查。
  2. 核心更新: 调用 wp_version_check() 函数,与 WordPress.org API 交互,检查核心更新。
  3. 插件更新: 调用 wp_update_plugins() 函数,与 WordPress 站点自身的 plugin-install.php 交互,检查插件更新。
  4. 主题更新: 调用 wp_update_themes() 函数,与 WordPress 站点自身的 theme-install.php 交互,检查主题更新。
  5. 存储: 将更新信息存储在瞬态中,以便后续使用。

与 WordPress.org API 交互的方式总结:

更新类型 API 端点 请求方法 请求体 响应格式
核心更新 https://api.wordpress.org/core/version-check/1.7/ POST 包含 WordPress 版本、PHP 版本、数据库版本、站点数量、用户数量等信息。 JSON,包含更新信息
插件更新 admin_url( 'plugin-install.php' ) POST 包含所有已安装的插件信息和激活插件信息。action 参数设置为 update-check 序列化的 PHP 对象,包含更新信息
主题更新 admin_url( 'theme-install.php' ) POST 包含所有已安装的主题信息。action 参数设置为 update-check 序列化的 PHP 对象,包含更新信息

一些思考:

  • 性能: 频繁的更新检查可能会影响网站性能。因此,WordPress 使用瞬态来缓存更新信息,并限制更新检查的频率。
  • 安全性: 更新检查过程涉及与外部 API 的通信,需要注意安全性问题。WordPress 使用 HTTPS 协议来保护通信安全。
  • 可扩展性: WordPress 允许通过过滤器来修改更新检查的行为,例如修改 API 端点、修改更新检查的频率等。

希望通过今天的讲解,你们对 WordPress 的更新检查机制有了更深入的了解。记住,保持你的 WordPress 网站更新,就像保持你的身体健康一样重要!下次再见!

发表回复

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