剖析 WordPress `wpmu_validate_blog_signup()` 函数的源码:在创建新站点时如何验证数据。

嘿,各位代码界的大佬、小萌新们,欢迎来到今天的 WordPress 源码剖析小课堂! 今天咱们要聊的是 WordPress 多站点模式下,新站点注册时至关重要的一个函数 —— wpmu_validate_blog_signup()。 别看名字长,其实它就是个“守门员”,负责把守新站点注册的大门,确保提交的数据符合规范,防止垃圾站点横行霸道。 准备好了吗? 咱们这就开扒!

一、wpmu_validate_blog_signup() 是个什么角色?

简单来说,wpmu_validate_blog_signup() 函数是 WordPress 多站点 (WordPress Multisite) 环境下,用户注册新站点时,服务器端用来验证用户输入数据的核心函数。 它的主要职责包括:

  • 检查站点地址 (domain) 是否合法: 比如是否符合域名格式、是否已被占用、是否是保留字等。
  • 检查站点标题 (title) 是否为空: 总不能让站点没名字就出生吧?
  • 检查用户邮箱 (user_email) 是否合法: 邮箱格式是否正确、是否已被注册等。
  • 返回验证结果: 告诉 WordPress 系统,这次注册是 "成功" 还是 "失败",如果失败,还要给出具体的原因。

二、 源码解读:一步一个脚印

wpmu_validate_blog_signup() 函数的源码位于 wp-includes/ms-functions.php 文件中。 咱们一点点地把它拆解开来,看看这位 "守门员" 到底是怎么工作的。

function wpmu_validate_blog_signup( $domain, $path, $title, $user_email, $errors = null ) {
    global $wpdb;

    $orig_domain = $domain;
    $filtered_domain = preg_replace( '/[^-a-z0-9]/', '', strtolower( $domain ) );
    if ( $filtered_domain != $domain ) {
        $errors->add( 'domain_invalid', __( 'Only lowercase letters, numbers, and hyphens allowed.' ) );
    }

    if ( strlen( $domain ) > 60 ) {
        $errors->add( 'domain_long', __( 'Site domain is too long, limit is 60 characters.' ) );
    }

    if ( ! is_subdomain_install() && strpos( $domain, '.' ) ) {
        $errors->add( 'domain_invalid', __( 'The domain may not contain periods.' ) );
    }

    if ( empty( $path ) ) {
        $errors->add( 'path_empty', __( 'Missing path.' ) );
    }

    if ( substr( $path, 0, 1 ) != '/' )
        $path = '/' . $path;
    if ( substr( $path, -1 ) != '/' )
        $path .= '/';

    $check_path = preg_replace( '/[^a-z0-9]/', '', strtolower( $path ) );
    if ( $check_path != substr( $path, 1, strlen( $path ) - 2 ) ) {
        $errors->add( 'path_invalid', __( 'Only lowercase letters and numbers allowed.' ) );
    }

    if ( strlen( $path ) > 60 ) {
        $errors->add( 'path_long', __( 'Site path is too long, limit is 60 characters.' ) );
    }

    $title = trim( $title );
    if ( empty( $title ) ) {
        $errors->add( 'title_empty', __( 'Missing title.' ) );
    }

    if ( empty( $user_email ) ) {
        $errors->add( 'email_empty', __( 'Missing email address.' ) );
    } elseif ( ! is_email( $user_email ) ) {
        $errors->add( 'email_invalid', __( 'Invalid email address.' ) );
    } else {
        if ( email_exists( $user_email ) ) {
            $errors->add( 'email_exists', __( 'This email is already registered, please choose another one.' ) );
        }
    }

    /**
     * Fires before site signup validation.
     *
     * @since 3.0.0
     *
     * @param string $domain     The requested domain.
     * @param string $path       The requested path.
     * @param string $title      The requested site title.
     * @param string $user_email The user email address.
     * @param WP_Error $errors     A WP_Error object containing any errors.
     */
    do_action( 'signup_blogform_validate', $domain, $path, $title, $user_email, $errors );

    $domain = $filtered_domain;

    return compact( 'domain', 'path', 'title', 'user_email', 'errors', 'orig_domain' );
}

2.1 函数签名和参数

首先,我们来看一下函数的签名:

function wpmu_validate_blog_signup( $domain, $path, $title, $user_email, $errors = null )
  • $domain: 用户希望的站点域名 (例如: mysite)。
  • $path: 用户希望的站点路径 (例如: /blog/)。
  • $title: 用户希望的站点标题 (例如: 我的小博客)。
  • $user_email: 用户的邮箱地址。
  • $errors: 一个 WP_Error 对象,用于存储验证过程中发现的错误。 如果没有传入,函数会创建一个新的 WP_Error 对象。

2.2 域名 (Domain) 验证

$orig_domain = $domain;
$filtered_domain = preg_replace( '/[^-a-z0-9]/', '', strtolower( $domain ) );
if ( $filtered_domain != $domain ) {
    $errors->add( 'domain_invalid', __( 'Only lowercase letters, numbers, and hyphens allowed.' ) );
}

if ( strlen( $domain ) > 60 ) {
    $errors->add( 'domain_long', __( 'Site domain is too long, limit is 60 characters.' ) );
}

if ( ! is_subdomain_install() && strpos( $domain, '.' ) ) {
    $errors->add( 'domain_invalid', __( 'The domain may not contain periods.' ) );
}

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

  1. 保存原始域名: 先把 $domain 的原始值存到 $orig_domain 中,后面可能会用到。
  2. 过滤非法字符: 使用正则表达式 /[^-a-z0-9]/ 移除域名中所有非小写字母、数字和连字符的字符。 并将域名转化为小写。
  3. 检查是否包含非法字符: 如果过滤后的域名 $filtered_domain 和原始域名 $domain 不一致,说明原始域名包含非法字符,就往 $errors 对象里添加一个错误信息。
  4. 检查域名长度: 限制域名长度不能超过 60 个字符,超过了就报错。
  5. 检查是否包含句点: 如果不是子域名安装模式 (is_subdomain_install() 返回 false),则域名中不能包含句点 (.),否则报错。 这种情况通常是子目录安装模式。

2.3 路径 (Path) 验证

if ( empty( $path ) ) {
    $errors->add( 'path_empty', __( 'Missing path.' ) );
}

if ( substr( $path, 0, 1 ) != '/' )
    $path = '/' . $path;
if ( substr( $path, -1 ) != '/' )
    $path .= '/';

$check_path = preg_replace( '/[^a-z0-9]/', '', strtolower( $path ) );
if ( $check_path != substr( $path, 1, strlen( $path ) - 2 ) ) {
    $errors->add( 'path_invalid', __( 'Only lowercase letters and numbers allowed.' ) );
}

if ( strlen( $path ) > 60 ) {
    $errors->add( 'path_long', __( 'Site path is too long, limit is 60 characters.' ) );
}

路径验证的代码逻辑如下:

  1. 检查路径是否为空: 如果 $path 为空,说明用户没有填写路径,报错。
  2. 确保路径以 / 开头和结尾: 如果 $path 不是以 / 开头,就在前面加上 /。 如果 $path 不是以 / 结尾,就在后面加上 /。 这样做的目的是为了保证路径的格式统一。
  3. 过滤非法字符: 使用正则表达式 /[^a-z0-9]/ 移除路径中所有非小写字母和数字的字符。
  4. 检查是否包含非法字符: 这里比较巧妙,先用 substr( $path, 1, strlen( $path ) - 2 ) 去掉了路径首尾的 /, 然后再和过滤后的路径 $check_path 进行比较。 如果不一致,说明路径中包含非法字符,报错。
  5. 检查路径长度: 限制路径长度不能超过 60 个字符,超过了就报错。

2.4 标题 (Title) 验证

$title = trim( $title );
if ( empty( $title ) ) {
    $errors->add( 'title_empty', __( 'Missing title.' ) );
}

标题验证的代码非常简单:

  1. 去除首尾空格: 使用 trim() 函数去除标题 $title 首尾的空格。
  2. 检查标题是否为空: 如果 $title 为空,说明用户没有填写标题,报错。

2.5 邮箱 (Email) 验证

if ( empty( $user_email ) ) {
    $errors->add( 'email_empty', __( 'Missing email address.' ) );
} elseif ( ! is_email( $user_email ) ) {
    $errors->add( 'email_invalid', __( 'Invalid email address.' ) );
} else {
    if ( email_exists( $user_email ) ) {
        $errors->add( 'email_exists', __( 'This email is already registered, please choose another one.' ) );
    }
}

邮箱验证的代码稍微复杂一些:

  1. 检查邮箱是否为空: 如果 $user_email 为空,说明用户没有填写邮箱,报错。
  2. 检查邮箱格式是否正确: 使用 is_email() 函数检查邮箱格式是否正确。 如果不正确,报错。
  3. 检查邮箱是否已被注册: 使用 email_exists() 函数检查邮箱是否已经被注册。 如果已被注册,报错。

2.6 do_action( 'signup_blogform_validate' )

/**
 * Fires before site signup validation.
 *
 * @since 3.0.0
 *
 * @param string $domain     The requested domain.
 * @param string $path       The requested path.
 * @param string $title      The requested site title.
 * @param string $user_email The user email address.
 * @param WP_Error $errors     A WP_Error object containing any errors.
 */
do_action( 'signup_blogform_validate', $domain, $path, $title, $user_email, $errors );

这行代码是一个 WordPress 的 Action Hook。 它的作用是:允许开发者在站点注册验证过程中的这个点,插入自定义的验证逻辑。 比如,你可以使用这个 Hook 来添加额外的安全验证,或者检查用户是否满足某些特殊条件。

2.7 返回值

$domain = $filtered_domain;

return compact( 'domain', 'path', 'title', 'user_email', 'errors', 'orig_domain' );

函数最后返回一个数组,包含了以下信息:

  • domain: 经过过滤后的域名。
  • path: 路径。
  • title: 标题。
  • user_email: 用户的邮箱地址。
  • errors: WP_Error 对象,包含了所有的错误信息。
  • orig_domain: 原始域名。

使用了 compact() 函数,它会创建一个关联数组,键名为变量名,值为变量的值。 这样方便调用者获取验证结果。

三、 WP_Error 对象:错误信息的容器

wpmu_validate_blog_signup() 函数中,我们看到了 WP_Error 对象的身影。 它是 WordPress 用来管理错误信息的工具。 我们可以使用它来:

  • 添加错误信息: 使用 $errors->add( $code, $message ) 方法,其中 $code 是错误代码 (例如:domain_invalid),$message 是错误信息 (例如:域名无效)。
  • 检查是否有错误: 使用 $errors->has_errors() 方法,如果返回 true,说明有错误。
  • 获取错误信息: 使用 $errors->get_error_messages() 方法,可以获取所有错误信息的数组。

四、 实例演示:模拟一次注册验证

为了更好地理解 wpmu_validate_blog_signup() 函数的工作流程,我们来模拟一次新站点注册,并手动调用这个函数进行验证。

<?php

// 引入 WordPress 的核心文件
require_once( 'wp-load.php' );

// 模拟用户输入的数据
$domain = 'My-Site.com'; // 故意包含大写字母和句点
$path = '/my_blog';  // 故意不以 / 结尾
$title = 'My Awesome Blog';
$user_email = '[email protected]';

// 创建一个 WP_Error 对象
$errors = new WP_Error();

// 调用 wpmu_validate_blog_signup 函数进行验证
$result = wpmu_validate_blog_signup( $domain, $path, $title, $user_email, $errors );

// 检查是否有错误
if ( $result['errors']->has_errors() ) {
    // 输出错误信息
    echo "注册失败:n";
    foreach ( $result['errors']->get_error_messages() as $message ) {
        echo "- " . $message . "n";
    }
} else {
    // 输出验证结果
    echo "注册成功!n";
    echo "域名:" . $result['domain'] . "n";
    echo "路径:" . $result['path'] . "n";
    echo "标题:" . $result['title'] . "n";
    echo "邮箱:" . $result['user_email'] . "n";
}

?>

运行这段代码,你会看到类似以下的输出:

注册失败:
- Only lowercase letters, numbers, and hyphens allowed.
- Missing path.

可以看到,wpmu_validate_blog_signup() 函数成功地检测到了域名和路径中的错误。

五、 总结与思考

wpmu_validate_blog_signup() 函数是 WordPress 多站点模式下,新站点注册流程中的重要一环。 它通过一系列的验证规则,确保用户输入的数据符合规范,从而保证整个系统的安全性和稳定性。

验证项 验证内容 失败时错误代码
域名 是否只包含小写字母、数字和连字符,长度是否超过 60,是否包含句点 (取决于安装模式) domain_invalid, domain_long
路径 是否为空,是否以 / 开头和结尾,是否只包含小写字母和数字,长度是否超过 60 path_empty, path_invalid, path_long
标题 是否为空 title_empty
邮箱 是否为空,格式是否正确,是否已被注册 email_empty, email_invalid, email_exists

通过对 wpmu_validate_blog_signup() 函数源码的剖析,我们不仅了解了它的工作原理,还学习了如何使用 WP_Error 对象来管理错误信息。 掌握了这些知识,你就能更好地理解 WordPress 多站点的工作机制,也能在实际开发中更加得心应手。

好了,今天的 WordPress 源码剖析小课堂就到这里了。 希望这次的讲解对你有所帮助。 咱们下次再见!

发表回复

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