剖析 WordPress `install_network()` 函数的源码:多站点网络的安装流程是怎样的。

各位观众老爷,晚上好! 今晚咱来聊聊 WordPress 多站点网络这事儿。说白了,就是在一个 WordPress 安装基础上,搞出N多个站点,共享核心文件,但各自有各自的地盘(数据库表、上传文件等等)。 听起来是不是有点像“一母生多子,各过各的日子”?

今天咱们要扒的就是 install_network() 这个函数,看看它是怎么把这 “一母多子” 的框架给搭起来的。准备好了吗?发车!

一、入场前的准备:环境检查与变量初始化

install_network() 正式开工之前,得先看看周围环境是否允许施工,以及准备好必要的材料。这段代码主要负责检查是否已经安装了WordPress,以及初始化一些重要的变量。

function install_network() {
    global $wpdb, $current_site, $wp_rewrite;

    if ( ! defined( 'WP_INSTALLING' ) ) {
        wp_die( __( 'This file cannot be accessed directly.' ) );
    }

    if ( is_multisite() ) {
        wp_die( __( 'WordPress is already installed as a network.' ) );
    }

    if ( ! current_user_can( 'setup_network' ) ) {
        wp_die( __( 'You do not have permission to use this page.' ) );
    }

    // 初始化一些变量
    $sitename = sanitize_title( get_bloginfo( 'name' ) );
    if ( empty( $sitename ) ) {
        $sitename = 'wp';
    }

    $admin_email = get_option( 'admin_email' );
    $path = trim( parse_url( get_bloginfo( 'url' ), PHP_URL_PATH ), '/' );

    // ... 后续代码
}

这段代码干了这些事:

  1. 防止直接访问: if ( ! defined( 'WP_INSTALLING' ) ) 这行确保这个文件只能通过 WordPress 安装程序调用,防止有人直接在浏览器里输入地址来搞破坏。
  2. 检查是否已是多站点: if ( is_multisite() ) 确认是否已经安装了多站点。如果已经安装了,那就没必要再安装了,直接报错退出。
  3. 权限验证: if ( ! current_user_can( 'setup_network' ) ) 只有拥有 setup_network 权限的用户才能安装多站点网络。一般来说,只有超级管理员才有这个权限。
  4. 初始化变量: 从站点名称 (get_bloginfo( 'name' )) 获取一个安全版本 (sanitize_title()) 作为网络名称的基础。如果站点名称为空,就使用 "wp" 作为默认值。同时,获取管理员邮箱地址 (get_option( 'admin_email' )) 和站点的路径 (parse_url( get_bloginfo( 'url' ), PHP_URL_PATH ))。

二、网络配置:域名类型与参数设置

接下来,install_network() 会根据用户选择的域名类型(子域名或子目录)进行一些配置。这是多站点网络的核心设置之一。

    if ( isset( $_POST['sitename'] ) ) {
        $sitename = sanitize_title( $_POST['sitename'] );
    }

    if ( ! isset( $_POST['subdomain_install'] ) ) {
        echo '<p>' . __( 'Choose whether you want sites to be created with subdomains or subdirectories.' ) . '</p>';
        echo '<p><label><input type="radio" name="subdomain_install" value="1" /> ' . sprintf( __( 'Sites in my WordPress network will use subdomains (like “site1.%s”).' ), $current_site->domain ) . '</label></p>';
        echo '<p><label><input type="radio" name="subdomain_install" value="0" /> ' . sprintf( __( 'Sites in my WordPress network will use subdirectories (like “%s/site1”).' ), $current_site->domain . $path ) . '</label></p>';
        return;
    }

    $subdomain_install = (bool) $_POST['subdomain_install'];

    if ( ! $wp_rewrite->using_mod_rewrite_permalinks() && ! $subdomain_install ) {
        echo '<div class="error">';
        echo '<p>' . __( 'You have chosen to use subdirectories. The installation will use subdomains if you proceed.' ) . '</p>';
        echo '</div>';
        $subdomain_install = true;
    }

这段代码做了以下工作:

  1. 接收域名类型选择:$_POST['subdomain_install'] 获取用户选择的域名类型。如果用户没有选择,就显示一个表单,让用户选择使用子域名还是子目录。
  2. 强制使用子域名: if ( ! $wp_rewrite->using_mod_rewrite_permalinks() && ! $subdomain_install ) 这段代码比较关键。它检查是否启用了 WordPress 的伪静态链接 (mod_rewrite)。如果 没有 启用,并且用户选择了 子目录,那么 WordPress 会强制使用子域名。这是因为子目录需要伪静态链接的支持才能正常工作。

三、数据库操作:表的创建与数据的插入

重头戏来了!install_network() 要开始往数据库里写东西了。它会创建一些新的表,或者修改现有的表,并插入一些必要的初始数据。

    // 创建/更新数据库表
    install_blog( $wpdb->siteid, $sitename, $admin_email, true );

    $site_id = $wpdb->siteid;
    $domain = preg_replace( '|^www.|', '', $current_site->domain );

    if ( $subdomain_install ) {
        $result = add_subdomain_install( $domain, $path );
    } else {
        $result = add_subdirectory_install( $domain, $path );
    }

    if ( ! $result ) {
        wp_die( __( 'There was an error adding the primary network site to the database.' ) );
    }

    update_site_option( $site_id, 'site_admins', array( get_userdata( get_current_user_id() )->user_login ) );

    // ... 后续代码

这里发生了这些事情:

  1. 创建/更新站点表: install_blog( $wpdb->siteid, $sitename, $admin_email, true ) 这个函数负责创建或更新站点表。注意这里的 $wpdb->siteid 通常是 1,代表主站点。install_blog() 的第四个参数 $is_main_site 设置为 true,表明这是主站点。
  2. 添加主站点到数据库: add_subdomain_install()add_subdirectory_install() 这两个函数根据用户选择的域名类型,将主站点的信息添加到 wp_sitemeta 表中。
  3. 设置站点管理员: update_site_option( $site_id, 'site_admins', array( get_userdata( get_current_user_id() )->user_login ) ) 设置主站点的管理员。这里的 get_current_user_id() 获取当前用户的 ID,get_userdata() 获取用户信息,然后提取用户名 (user_login)。

四、修改 .htaccess 和 wp-config.php:规则调整与配置更新

多站点网络需要对 .htaccess 文件和 wp-config.php 文件进行一些修改,才能正常工作。install_network() 会提示用户手动修改这些文件。

    // 显示需要添加到 .htaccess 和 wp-config.php 的代码
    $ms_constant = ( $subdomain_install ) ? 'SUBDOMAIN_INSTALL' : 'SUBDIR_INSTALL';

    $wp_config_code = "
define( 'WP_ALLOW_MULTISITE', true );
define( '$ms_constant', true );
define( 'MULTISITE', true );
define( 'DOMAIN_CURRENT_SITE', '" . $domain . "' );
define( 'PATH_CURRENT_SITE', '" . $path . "' );
define( 'SITE_ID_CURRENT_SITE', " . $site_id . " );
define( 'BLOG_ID_CURRENT_SITE', " . $wpdb->blogid . " );
";

    $subdir_htaccess = "
RewriteEngine On
RewriteBase " . $path . "/
RewriteRule ^index.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^wp-admin$ wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(" . $wp_rewrite->rewrite_rules_slug . ".*) wp-includes/ms-files.php?file=$1 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*.php)$ $2 [L]
RewriteRule . index.php [L]
";

    $subdomain_htaccess = "
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^wp-admin$ wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(" . $wp_rewrite->rewrite_rules_slug . ".*) wp-includes/ms-files.php?file=$1 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*.php)$ $2 [L]
RewriteRule . index.php [L]
";

    echo '<h2>' . __( 'Creating a Network of WordPress Sites' ) . '</h2>';
    echo '<p>' . __( 'The following lines go in your <code>wp-config.php</code> file:' ) . '</p>';
    echo '<p><code>';
    echo str_replace( ' ', '&nbsp;', esc_html( $wp_config_code ) );
    echo '</code></p>';

    if ( $subdomain_install ) {
        echo '<p>' . __( 'Add the following code to your <code>.htaccess</code> file in the root directory. Make sure you have a backup of your <code>.htaccess</code> file before you do:' ) . '</p>';
        echo '<p><code>';
        echo str_replace( ' ', '&nbsp;', esc_html( $subdomain_htaccess ) );
        echo '</code></p>';
    } else {
        echo '<p>' . __( 'Add the following code to your <code>.htaccess</code> file in the root directory. Make sure you have a backup of your <code>.htaccess</code> file before you do:' ) . '</p>';
        echo '<p><code>';
        echo str_replace( ' ', '&nbsp;', esc_html( $subdir_htaccess ) );
        echo '</code></p>';
    }

这段代码主要做了以下事情:

  1. 定义常量: 根据域名类型,定义 SUBDOMAIN_INSTALLSUBDIR_INSTALL 常量。
  2. 生成 wp-config.php 代码: 生成需要添加到 wp-config.php 文件的代码,包括 WP_ALLOW_MULTISITEMULTISITEDOMAIN_CURRENT_SITEPATH_CURRENT_SITESITE_ID_CURRENT_SITE 等常量。
  3. 生成 .htaccess 代码: 根据域名类型,生成需要添加到 .htaccess 文件的代码。这些代码主要负责重写 URL,将子域名或子目录的请求转发到相应的站点。
  4. 显示代码: 将生成的代码显示给用户,让用户手动添加到 wp-config.php.htaccess 文件中。

注意:WordPress 并没有自动修改这两个文件,而是让用户手动修改。这是为了安全起见,防止 WordPress 因为权限问题而无法修改这些文件,导致安装失败。

五、收尾工作:提示与重定向

安装完成后,install_network() 会提示用户重新登录,并重定向到网络管理界面。

    echo '<p>' . __( 'Once you have completed putting the code into your <code>wp-config.php</code> and <code>.htaccess</code> files, log in again.' ) . '</p>';
    echo '<p><a href="' . esc_url( wp_login_url() ) . '">' . __( 'Log In' ) . '</a></p>';
}

这段代码很简单,就是显示一条提示信息,告诉用户需要重新登录,并提供一个登录链接。重新登录后,用户就可以进入网络管理界面,开始管理多站点网络了。

总结:install_network() 的核心流程

为了方便大家理解,我把 install_network() 的核心流程总结成一个表格:

步骤 描述 主要代码
1. 环境检查与变量初始化 检查是否已安装 WordPress,是否已是多站点,以及用户权限。初始化站点名称、管理员邮箱地址和站点路径等变量。 if ( ! defined( 'WP_INSTALLING' ) ), if ( is_multisite() ), if ( ! current_user_can( 'setup_network' ) ), $sitename = sanitize_title( get_bloginfo( 'name' ) )
2. 网络配置 根据用户选择的域名类型(子域名或子目录)进行配置。如果未启用伪静态链接且用户选择子目录,则强制使用子域名。 if ( isset( $_POST['subdomain_install'] ) ), if ( ! $wp_rewrite->using_mod_rewrite_permalinks() && ! $subdomain_install )
3. 数据库操作 创建/更新站点表,添加主站点信息到数据库,设置站点管理员。 install_blog( $wpdb->siteid, $sitename, $admin_email, true ), add_subdomain_install(), add_subdirectory_install(), update_site_option( $site_id, 'site_admins', array( get_userdata( get_current_user_id() )->user_login ) )
4. 修改文件 生成需要添加到 wp-config.php.htaccess 文件的代码,并显示给用户,让用户手动修改。 $ms_constant = ( $subdomain_install ) ? 'SUBDOMAIN_INSTALL' : 'SUBDIR_INSTALL', 生成 $wp_config_code$subdir_htaccess$subdomain_htaccess
5. 收尾工作 提示用户重新登录,并重定向到网络管理界面。 echo '<p>' . __( 'Once you have completed putting the code into your <code>wp-config.php</code> and <code>.htaccess</code> files, log in again.' ) . '</p>'

一些需要注意的点:

  • 伪静态链接 (mod_rewrite): 子目录模式依赖于伪静态链接,如果服务器不支持,或者没有正确配置,可能会导致多站点网络无法正常工作。
  • .htaccess 文件权限: 确保 WordPress 能够读取和写入 .htaccess 文件,否则多站点网络的 URL 重写规则可能无法生效。
  • 数据库表前缀: 多站点网络共享数据库表,但每个站点都有自己的表前缀。确保表前缀设置正确,避免数据冲突。
  • 域名解析: 如果是子域名模式,需要确保所有子域名都解析到服务器的 IP 地址。

好了,关于 install_network() 函数的源码剖析就到这里了。希望通过这次讲座,大家对 WordPress 多站点网络的安装流程有了更深入的了解。记住,源码才是最可靠的老师,多看多练,你也能成为 WordPress 大牛!

下次再见!

发表回复

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