文章目录
-
- 在当今互联网环境中,网站表单是与用户互动的重要桥梁。无论是联系表单、注册表单、订单表单还是调查问卷,表单数据的质量直接影响到用户体验、数据分析和业务决策。然而,许多WordPress网站管理员面临一个共同问题:如何确保用户提交的表单数据既准确又安全? 传统的表单验证方法往往只停留在基础层面,如检查必填字段或简单的格式验证。但随着网络攻击手段的日益复杂和用户期望的提高,我们需要更智能的解决方案。本文将手把手教您如何通过WordPress代码二次开发,集成智能化的表单数据验证与清洗工具,将常用互联网小工具功能融入您的网站。
-
- WordPress生态系统中有众多表单插件,如Contact Form 7、Gravity Forms、WPForms和Ninja Forms等。这些插件提供了丰富的表单构建功能,但在数据验证和清洗方面往往存在局限性: 基础验证功能:大多数插件提供必填字段、电子邮件格式、数字范围等基础验证 有限的清洗能力:对用户输入的潜在危险内容过滤不足 缺乏智能验证:无法识别虚假信息、垃圾内容或恶意输入 扩展性有限:自定义验证规则需要复杂的配置或额外插件
- 在集成智能验证工具前,我们需要了解表单数据面临的主要风险: 安全威胁:SQL注入、跨站脚本攻击(XSS)、跨站请求伪造(CSRF) 数据质量问题:虚假信息、格式错误、不一致的数据 垃圾信息:广告内容、垃圾邮件、机器人提交 用户体验问题:复杂的验证流程、不清晰的错误提示
- 智能表单验证与清洗不仅仅是检查数据格式,还包括: 上下文感知验证:根据字段类型和业务逻辑进行验证 实时反馈:在用户输入时提供即时验证反馈 数据标准化:将不同格式的数据转换为统一格式 威胁检测:识别并阻止恶意内容提交 垃圾信息过滤:使用多种技术识别和过滤垃圾提交
-
- 在开始二次开发前,确保您已准备好以下环境: 本地开发环境:推荐使用Local by Flywheel、XAMPP或MAMP 代码编辑器:VS Code、PHPStorm或Sublime Text WordPress安装:最新版本的WordPress 调试工具:安装Query Monitor、Debug Bar等调试插件 版本控制:Git用于代码版本管理
- 为了避免主题更新导致代码丢失,我们将创建一个独立插件来实现表单验证功能: 在wp-content/plugins/目录下创建新文件夹smart-form-validation 创建主插件文件smart-form-validation.php,添加插件头部信息: <?php /** * Plugin Name: Smart Form Validation & Sanitization * Plugin URI: https://yourwebsite.com/ * Description: 智能表单验证与清洗工具,增强WordPress表单安全性 * Version: 1.0.0 * Author: Your Name * License: GPL v2 or later * Text Domain: smart-form-validation */
- 在进行二次开发时,请牢记以下安全准则: 始终对用户输入进行验证、清洗和转义 使用WordPress非ce和权限检查功能 避免直接执行用户提供的代码 定期更新和维护您的自定义代码
-
- 我们将创建一个核心验证类,包含常用的验证方法: class Smart_Form_Validator { private $errors = array(); private $cleaned_data = array(); /** * 验证电子邮件地址 */ public function validate_email($email, $field_name = 'email') { if (empty($email)) { $this->add_error($field_name, __('电子邮件地址不能为空', 'smart-form-validation')); return false; } // 使用WordPress内置函数验证邮箱格式 if (!is_email($email)) { $this->add_error($field_name, __('请输入有效的电子邮件地址', 'smart-form-validation')); return false; } // 检查邮箱域名是否真实存在 if (apply_filters('smart_form_check_email_domain', true)) { list($user, $domain) = explode('@', $email); if (!checkdnsrr($domain, 'MX')) { $this->add_error($field_name, __('电子邮件域名无效', 'smart-form-validation')); return false; } } $this->cleaned_data[$field_name] = sanitize_email($email); return true; } /** * 验证电话号码 */ public function validate_phone($phone, $field_name = 'phone', $country_code = 'CN') { $phone = preg_replace('/s+/', '', $phone); // 中国手机号验证 if ($country_code === 'CN') { if (!preg_match('/^1[3-9]d{9}$/', $phone)) { $this->add_error($field_name, __('请输入有效的中国手机号码', 'smart-form-validation')); return false; } } // 国际电话验证(简化版) else { if (!preg_match('/^+?[1-9]d{1,14}$/', $phone)) { $this->add_error($field_name, __('请输入有效的国际电话号码', 'smart-form-validation')); return false; } } $this->cleaned_data[$field_name] = sanitize_text_field($phone); return true; } /** * 验证URL */ public function validate_url($url, $field_name = 'url') { if (empty($url)) { return true; // URL字段可为空 } // 检查URL格式 if (!filter_var($url, FILTER_VALIDATE_URL)) { $this->add_error($field_name, __('请输入有效的URL地址', 'smart-form-validation')); return false; } // 检查URL是否安全 $parsed_url = parse_url($url); $blacklist_domains = apply_filters('smart_form_url_blacklist', array()); if (in_array($parsed_url['host'], $blacklist_domains)) { $this->add_error($field_name, __('该URL已被列入黑名单', 'smart-form-validation')); return false; } $this->cleaned_data[$field_name] = esc_url_raw($url); return true; } /** * 添加错误信息 */ private function add_error($field, $message) { $this->errors[$field] = $message; } /** * 获取所有错误 */ public function get_errors() { return $this->errors; } /** * 获取清洗后的数据 */ public function get_cleaned_data() { return $this->cleaned_data; } /** * 检查是否有错误 */ public function has_errors() { return !empty($this->errors); } }
- 除了基础验证,我们还需要实现更智能的验证功能: /** * 智能文本内容验证 */ public function validate_smart_text($text, $field_name, $options = array()) { $defaults = array( 'min_length' => 0, 'max_length' => 0, 'allow_html' => false, 'block_spam_keywords' => true, 'check_duplicate' => false, ); $options = wp_parse_args($options, $defaults); // 检查长度 $text_length = mb_strlen($text, 'UTF-8'); if ($options['min_length'] > 0 && $text_length < $options['min_length']) { $this->add_error($field_name, sprintf(__('内容至少需要%d个字符', 'smart-form-validation'), $options['min_length'])); return false; } if ($options['max_length'] > 0 && $text_length > $options['max_length']) { $this->add_error($field_name, sprintf(__('内容不能超过%d个字符', 'smart-form-validation'), $options['max_length'])); return false; } // 垃圾关键词检测 if ($options['block_spam_keywords']) { $spam_keywords = $this->get_spam_keywords(); foreach ($spam_keywords as $keyword) { if (stripos($text, $keyword) !== false) { $this->add_error($field_name, __('内容包含不被允许的关键词', 'smart-form-validation')); return false; } } } // 重复内容检测 if ($options['check_duplicate']) { $hash = md5($text); $existing_hashes = get_option('smart_form_submission_hashes', array()); // 清理过期的哈希值(24小时前的提交) $one_day_ago = time() - 86400; foreach ($existing_hashes as $timestamp => $content_hash) { if ($timestamp < $one_day_ago) { unset($existing_hashes[$timestamp]); } } if (in_array($hash, $existing_hashes)) { $this->add_error($field_name, __('检测到重复提交的内容', 'smart-form-validation')); return false; } // 存储当前提交的哈希值 $existing_hashes[time()] = $hash; update_option('smart_form_submission_hashes', $existing_hashes); } // 数据清洗 if ($options['allow_html']) { // 允许有限的HTML标签 $allowed_tags = wp_kses_allowed_html('post'); $cleaned_text = wp_kses($text, $allowed_tags); } else { // 完全清除HTML $cleaned_text = sanitize_textarea_field($text); } $this->cleaned_data[$field_name] = $cleaned_text; return true; } /** * 获取垃圾关键词列表 */ private function get_spam_keywords() { $default_keywords = array( 'viagra', 'cialis', 'casino', 'loan', 'mortgage', '赚钱', '赌博', '色情', '代开发票', '信用卡套现' ); return apply_filters('smart_form_spam_keywords', $default_keywords); }
- 为了提高用户体验,我们可以添加实时验证功能: /** * 注册AJAX验证端点 */ public function register_ajax_handlers() { add_action('wp_ajax_smart_form_validate_field', array($this, 'ajax_validate_field')); add_action('wp_ajax_nopriv_smart_form_validate_field', array($this, 'ajax_validate_field')); } /** * AJAX字段验证处理 */ public function ajax_validate_field() { // 安全检查 check_ajax_referer('smart_form_validation_nonce', 'nonce'); $field_name = sanitize_text_field($_POST['field_name'] ?? ''); $field_value = $_POST['field_value'] ?? ''; $field_type = sanitize_text_field($_POST['field_type'] ?? 'text'); $validator = new Smart_Form_Validator(); $is_valid = false; $message = ''; switch ($field_type) { case 'email': $is_valid = $validator->validate_email($field_value, $field_name); break; case 'phone': $is_valid = $validator->validate_phone($field_value, $field_name); break; case 'url': $is_valid = $validator->validate_url($field_value, $field_name); break; default: $is_valid = true; } if (!$is_valid) { $errors = $validator->get_errors(); $message = $errors[$field_name] ?? __('字段验证失败', 'smart-form-validation'); } wp_send_json(array( 'valid' => $is_valid, 'message' => $message, 'cleaned_value' => $validator->get_cleaned_data()[$field_name] ?? $field_value )); }
-
- 数据清洗不仅仅是去除危险字符,还包括标准化和规范化: /** * 深度数据清洗类 */ class Smart_Data_Sanitizer { /** * 清洗用户输入数组 */ public static function sanitize_array($data, $rules = array()) { $sanitized = array(); foreach ($data as $key => $value) { $rule = $rules[$key] ?? 'text'; if (is_array($value)) { $sanitized[$key] = self::sanitize_array($value, $rules); } else { $sanitized[$key] = self::sanitize_field($value, $rule); } } return $sanitized; } /** * 根据规则清洗单个字段 */ public static function sanitize_field($value, $rule = 'text') { switch ($rule) { case 'email': return sanitize_email($value); case 'url': return esc_url_raw($value); case 'textarea': return sanitize_textarea_field($value); case 'html': $allowed_tags = wp_kses_allowed_html('post'); return wp_kses($value, $allowed_tags); case 'integer': return intval($value); case 'float': return floatval($value); case 'date': // 尝试解析日期并格式化为Y-m-d $timestamp = strtotime($value); return $timestamp ? date('Y-m-d', $timestamp) : ''; case 'phone': // 移除所有非数字字符,除了开头的+ $cleaned = preg_replace('/[^d+]/', '', $value); return substr($cleaned, 0, 20); // 限制长度 case 'credit_card': // 只保留数字,并添加掩码 $cleaned = preg_replace('/D/', '', $value); if (strlen($cleaned) >= 4) { return '**** **** **** ' . substr($cleaned, -4); } return $cleaned; case 'price': // 格式化价格,保留两位小数 $cleaned = preg_replace('/[^d.]/', '', $value); $float_value = floatval($cleaned); return number_format($float_value, 2, '.', ''); default: return sanitize_text_field($value); } } /** * 防止SQL注入 */ public static function prevent_sql_injection($input) { global $wpdb; // 使用$wpdb->prepare进行查询参数化 // 这里我们主要进行输入验证 $dangerous_patterns = array( '/unions+select/i', '/inserts+into/i', '/updates+.+set/i', '/deletes+from/i', '/drops+table/i', '/--/', '/#/', '//*/', '/*//', '/waitfors+delay/i', '/benchmark(/i' ); foreach ($dangerous_patterns as $pattern) { if (preg_match($pattern, $input)) { return ''; // 发现危险内容,返回空字符串 } } return $input; } /** * 防止XSS攻击 */ public static function prevent_xss($input) { // 移除危险的HTML属性和事件处理器 $dangerous_attributes = array( 'onload', 'onerror', 'onclick', 'onmouseover', 'onmouseout', 'onkeydown', 'onkeypress', 'onkeyup', 'javascript:', 'vbscript:', 'expression(' ); foreach ($dangerous_attributes as $attr) { $input = preg_replace('/' . preg_quote($attr, '/') . 's*:/i', 'blocked:', $input); $input = preg_replace('/' . preg_quote($attr, '/') . 's*=/i', 'blocked=', $input); } return $input; } }
- /** * 垃圾信息检测类 */ class Smart_Spam_Detector { private $score = 0; private $threshold = 5; // 超过此分数视为垃圾信息 /** * 检测提交是否为垃圾信息 */ public function is_spam($data) { $this->score = 0; // 检查提交频率 $this->check_submission_frequency(); // 检查隐藏蜜罐字段 $this->check_honeypot($data); // 检查内容特征 if (isset($data['message'])) { $this->check_content_features($data['message']); } // 检查链接数量 $this->check_link_count($data); // 检查用户代理 $this->check_user_agent(); // 允许其他插件修改分数 $this->score = apply_filters('smart_form_spam_score', $this->score, $data); return $this->score >= $this->threshold; } /** * 检查提交频率 */ private function check_submission_frequency() { $ip = $this->get_user_ip(); $transient_key = 'smart_form_submission_' . md5($ip); $submission_count = get_transient($transient_key); if ($submission_count === false) { $submission_count = 0; set_transient($transient_key, 1, 300); // 5分钟限制 } else { $submission_count++; set_transient($transient_key, $submission_count, 300); // 短时间内多次提交增加垃圾分数 if ($submission_count > 3) { $this->score += ($submission_count - 2) * 2; } } } /** * 检查蜜罐字段 */ private function check_honeypot($data) { // 蜜罐字段名,对用户不可见 $honeypot_field = apply_filters('smart_form_honeypot_field', 'website_url'); if (isset($data[$honeypot_field]) && !empty($data[$honeypot_field])) { // 蜜罐字段被填写,很可能是机器人 $this->score += 10; } } /** * 检查内容特征 */ private function check_content_features($content) { // 检查大写字母比例 $total_chars = strlen($content); $uppercase_chars = preg_match_all('/[A-Z]/', $content); if ($total_chars > 10) { $uppercase_ratio = $uppercase_chars / $total_chars; if ($uppercase_ratio > 0.5) { $this->score += 3; // 过多大写字母 } } // 检查垃圾关键词 $spam_keywords = array( 'viagra', 'cialis', 'casino', 'loan', 'mortgage', '赚钱', '赌博', '色情', '代开发票', '信用卡套现' ); foreach ($spam_keywords as $keyword) { if (stripos($content, $keyword) !== false) { $this->score += 5; break; } } // 检查无意义重复 if ($this->has_repetitive_pattern($content)) { $this->score += 4; } } /** * 检查链接数量 */ private function check_link_count($data) { $total_links = 0; foreach ($data as $value) { if (is_string($value)) { // 简单统计链接数量 $links = preg_match_all('/https?://[^s]+/', $value, $matches); $total_links += $links; } } if ($total_links > 2) { $this->score += $total_links; // 每个链接加1分 } } /** * 检查用户代理 */ private function check_user_agent() { $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; if (empty($user_agent)) { $this->score += 3; // 空用户代理 return; } // 已知的垃圾机器人用户代理 $spam_bots = array( 'bot', 'crawler', 'spider', 'scraper', 'curl', 'wget', 'python-requests', 'java' ); foreach ($spam_bots as $bot) { if (stripos($user_agent, $bot) !== false) { $this->score += 2; break; } } } /** * 获取用户IP */ private function get_user_ip() { $ip = ''; if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip = $_SERVER['REMOTE_ADDR'] ?? ''; } return sanitize_text_field($ip); } /** * 检查是否有重复模式 */ private function has_repetitive_pattern($content) { // 检查连续重复的单词 if (preg_match('/(bw+b)(?:s+1){2,}/i', $content)) { return true; } // 检查重复字符 if (preg_match('/(.)1{5,}/', $content)) { return true; } return false; } }
-
- /** * 地址智能处理类 */ class Smart_Address_Processor { /** * 集成第三方地址API进行验证 */ public static function validate_address($address, $country = 'CN') { $result = array( 'valid' => false, 'normalized' => '', 'components' => array(), 'suggestions' => array() ); // 基础格式验证 if (empty($address) || strlen($address) < 5) { return $result; } // 使用百度地图API进行地址验证(示例) if ($country === 'CN' && defined('SMART_FORM_BAIDU_MAP_AK')) { $api_result = self::validate_via_baidu_map($address); if ($api_result) { return $api_result; } } // 本地规则验证 return self::validate_locally($address, $country); } /** * 通过百度地图API验证地址 */ private static function validate_via_baidu_map($address) { $api_key = SMART_FORM_BAIDU_MAP_AK; $url = "http://api.map.baidu.com/geocoding/v3/?address=" . urlencode($address) . "&output=json&ak=" . $api_key; $response = wp_remote_get($url, array('timeout' => 5)); if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if ($data && $data['status'] == 0) { return array( 'valid' => true, 'normalized' => $data['result']['formatted_address'] ?? $address, 'components' => array( 'province' => $data['result']['addressComponent']['province'] ?? '', 'city' => $data['result']['addressComponent']['city'] ?? '', 'district' => $data['result']['addressComponent']['district'] ?? '', 'street' => $data['result']['addressComponent']['street'] ?? '' ), 'location' => array( 'lng' => $data['result']['location']['lng'] ?? '', 'lat' => $data['result']['location']['lat'] ?? '' ) ); } return false; } /** * 本地地址验证规则 */ private static function validate_locally($address, $country) { $result = array( 'valid' => false, 'normalized' => $address, 'components' => array() ); // 中国地址验证规则 if ($country === 'CN') { // 检查是否包含必要的地址元素 $required_elements = array('省', '市', '区', '路', '街', '号'); $found_elements = 0; foreach ($required_elements as $element) { if (mb_strpos($address, $element) !== false) { $found_elements++; } } $result['valid'] = $found_elements >= 2; // 尝试提取地址组件 if (preg_match('/(.*?[省市])(.*?[市区县])(.*?[路街道])(.*)/', $address, $matches)) { $result['components'] = array( 'province' => $matches[1] ?? '', 'city' => $matches[2] ?? '', 'district' => $matches[3] ?? '', 'detail' => $matches[4] ?? '' ); } } return $result; } /** * 地址自动补全 */ public static function autocomplete_address($input, $country = 'CN') { $suggestions = array(); // 这里可以集成第三方地址补全API // 示例:使用本地地址数据库 $address_database = get_option('smart_form_address_db', array()); if (!empty($address_database)) { foreach ($address_database as $address) { if (stripos($address, $input) !== false) { $suggestions[] = $address; if (count($suggestions) >= 5) { break; } } } } return apply_filters('smart_form_address_suggestions', $suggestions, $input, $country); } }
- /** * 身份证验证类 */ class Smart_ID_Validator { /** * 验证中国身份证号码 */ public static function validate_chinese_id($id_number) { $id_number = strtoupper(trim($id_number)); // 基本格式验证 if (!preg_match('/^d{17}[dX]$/', $id_number)) { return array( 'valid' => false, 'error' => '身份证格式不正确', 'details' => array() ); } // 验证校验码 if (!self::verify_check_code($id_number)) { return array( 'valid' => false, 'error' => '身份证校验码错误', 'details' => array() ); } // 提取信息 $details = self::extract_id_info($id_number); // 验证出生日期 if (!checkdate($details['month'], $details['day'], $details['year'])) { return array( 'valid' => false, 'error' => '身份证出生日期无效', 'details' => $details ); } // 验证地区代码(前6位) if (!self::validate_region_code(substr($id_number, 0, 6))) { return array( 'valid' => false, 'error' => '身份证地区代码无效', 'details' => $details ); } return array( 'valid' => true, 'error' => '', 'details' => $details ); } /** * 验证校验码 */ private static function verify_check_code($id_number) { if (strlen($id_number) != 18) { return false; } // 加权因子 $weight_factors = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); // 校验码对应值 $check_codes = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); $sum = 0; for ($i = 0; $i < 17; $i++) { $sum += intval($id_number[$i]) * $weight_factors[$i]; } $mod = $sum % 11; $check_code = $check_codes[$mod]; return $check_code == $id_number[17]; } /** * 从身份证提取信息 */ private static function extract_id_info($id_number) { // 地区代码 $region_code = substr($id_number, 0, 6); // 出生日期 $birth_year = substr($id_number, 6, 4); $birth_month = substr($id_number, 10, 2); $birth_day = substr($id_number, 12, 2); // 顺序码 $sequence_code = substr($id_number, 14, 3); // 性别(顺序码奇数为男,偶数为女) $gender_code = intval($sequence_code); $gender = ($gender_code % 2 == 1) ? '男' : '女'; // 年龄计算 $current_year = date('Y'); $age = $current_year - $birth_year; // 判断是否已过生日 $current_month = date('m'); $current_day = date('d'); if ($current_month < $birth_month || ($current_month == $birth_month && $current_day < $birth_day)) { $age--; } return array( 'region_code' => $region_code, 'birth_date' => $birth_year . '-' . $birth_month . '-' . $birth_day, 'year' => intval($birth_year), 'month' => intval($birth_month), 'day' => intval($birth_day), 'sequence_code' => $sequence_code, 'gender' => $gender, 'age' => $age ); } /** * 验证地区代码 */ private static function validate_region_code($region_code) { // 这里可以集成地区代码数据库 // 简化版:只验证基本格式 $valid_prefixes = array( '11', '12', '13', '14', '15', // 华北 '21', '22', '23', // 东北 '31', '32', '33', '34', // 华东 '35', '36', '37', // 华中 '41', '42', '43', '44', '45', '46', // 华南 '50', '51', '52', '53', '54', // 西南 '61', '62', '63', '64', '65', // 西北 '71', '81', '82' // 港澳台 ); $prefix = substr($region_code, 0, 2); return in_array($prefix, $valid_prefixes); } }
- /** * 银行卡验证类 */ class Smart_Bankcard_Validator { /** * 验证银行卡号 */ public static function validate_bankcard($card_number) { $card_number = preg_replace('/s+/', '', $card_number); // 基本格式验证 if (!preg_match('/^d{13,19}$/', $card_number)) { return array( 'valid' => false, 'error' => '银行卡号格式不正确', 'details' => array() ); } // Luhn算法验证 if (!self::validate_luhn($card_number)) { return array( 'valid' => false, 'error' => '银行卡号校验失败', 'details' => array() ); } // 识别银行和卡类型 $details = self::identify_bankcard($card_number); return array( 'valid' => true, 'error' => '', 'details' => $details ); } /** * Luhn算法验证 */ private static function validate_luhn($card_number) { $sum = 0; $length = strlen($card_number); $parity = $length % 2; for ($i = 0; $i < $length; $i++) { $digit = intval($card_number[$i]); if ($i % 2 == $parity) { $digit *= 2; if ($digit > 9) { $digit -= 9; } } $sum += $digit; } return ($sum % 10) == 0; } /** * 识别银行卡信息 */ private static function identify_bankcard($card_number) { $bin_codes = array( // 借记卡 '622848' => array('bank' => '农业银行', 'type' => '借记卡'), '622700' => array('bank' => '建设银行', 'type' => '借记卡'), '622262' => array('bank' => '交通银行', 'type' => '借记卡'), '622588' => array('bank' => '招商银行', 'type' => '借记卡'), '622760' => array('bank' => '中国银行', 'type' => '借记卡'), '622202' => array('bank' => '工商银行', 'type' => '借记卡'), // 信用卡 '438088' => array('bank' => '建设银行', 'type' => '信用卡'), '518710' => array('bank' => '招商银行', 'type' => '信用卡'), '622155' => array('bank' => '工商银行', 'type' => '信用卡'), '622156' => array('bank' => '工商银行', 'type' => '信用卡'), // 更多BIN码可以继续添加 ); $details = array( 'bank' => '未知', 'type' => '未知', 'length' => strlen($card_number), 'bin' => substr($card_number, 0, 6) ); // 检查前6位BIN码 foreach ($bin_codes as $bin => $info) {
在当今互联网环境中,网站表单是与用户互动的重要桥梁。无论是联系表单、注册表单、订单表单还是调查问卷,表单数据的质量直接影响到用户体验、数据分析和业务决策。然而,许多WordPress网站管理员面临一个共同问题:如何确保用户提交的表单数据既准确又安全?
传统的表单验证方法往往只停留在基础层面,如检查必填字段或简单的格式验证。但随着网络攻击手段的日益复杂和用户期望的提高,我们需要更智能的解决方案。本文将手把手教您如何通过WordPress代码二次开发,集成智能化的表单数据验证与清洗工具,将常用互联网小工具功能融入您的网站。
WordPress生态系统中有众多表单插件,如Contact Form 7、Gravity Forms、WPForms和Ninja Forms等。这些插件提供了丰富的表单构建功能,但在数据验证和清洗方面往往存在局限性:
- 基础验证功能:大多数插件提供必填字段、电子邮件格式、数字范围等基础验证
- 有限的清洗能力:对用户输入的潜在危险内容过滤不足
- 缺乏智能验证:无法识别虚假信息、垃圾内容或恶意输入
- 扩展性有限:自定义验证规则需要复杂的配置或额外插件
在集成智能验证工具前,我们需要了解表单数据面临的主要风险:
- 安全威胁:SQL注入、跨站脚本攻击(XSS)、跨站请求伪造(CSRF)
- 数据质量问题:虚假信息、格式错误、不一致的数据
- 垃圾信息:广告内容、垃圾邮件、机器人提交
- 用户体验问题:复杂的验证流程、不清晰的错误提示
智能表单验证与清洗不仅仅是检查数据格式,还包括:
- 上下文感知验证:根据字段类型和业务逻辑进行验证
- 实时反馈:在用户输入时提供即时验证反馈
- 数据标准化:将不同格式的数据转换为统一格式
- 威胁检测:识别并阻止恶意内容提交
- 垃圾信息过滤:使用多种技术识别和过滤垃圾提交
在开始二次开发前,确保您已准备好以下环境:
- 本地开发环境:推荐使用Local by Flywheel、XAMPP或MAMP
- 代码编辑器:VS Code、PHPStorm或Sublime Text
- WordPress安装:最新版本的WordPress
- 调试工具:安装Query Monitor、Debug Bar等调试插件
- 版本控制:Git用于代码版本管理
为了避免主题更新导致代码丢失,我们将创建一个独立插件来实现表单验证功能:
- 在
wp-content/plugins/目录下创建新文件夹smart-form-validation - 创建主插件文件
smart-form-validation.php,添加插件头部信息:
<?php
/**
* Plugin Name: Smart Form Validation & Sanitization
* Plugin URI: https://yourwebsite.com/
* Description: 智能表单验证与清洗工具,增强WordPress表单安全性
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
* Text Domain: smart-form-validation
*/
在进行二次开发时,请牢记以下安全准则:
- 始终对用户输入进行验证、清洗和转义
- 使用WordPress非ce和权限检查功能
- 避免直接执行用户提供的代码
- 定期更新和维护您的自定义代码
我们将创建一个核心验证类,包含常用的验证方法:
class Smart_Form_Validator {
private $errors = array();
private $cleaned_data = array();
/**
* 验证电子邮件地址
*/
public function validate_email($email, $field_name = 'email') {
if (empty($email)) {
$this->add_error($field_name, __('电子邮件地址不能为空', 'smart-form-validation'));
return false;
}
// 使用WordPress内置函数验证邮箱格式
if (!is_email($email)) {
$this->add_error($field_name, __('请输入有效的电子邮件地址', 'smart-form-validation'));
return false;
}
// 检查邮箱域名是否真实存在
if (apply_filters('smart_form_check_email_domain', true)) {
list($user, $domain) = explode('@', $email);
if (!checkdnsrr($domain, 'MX')) {
$this->add_error($field_name, __('电子邮件域名无效', 'smart-form-validation'));
return false;
}
}
$this->cleaned_data[$field_name] = sanitize_email($email);
return true;
}
/**
* 验证电话号码
*/
public function validate_phone($phone, $field_name = 'phone', $country_code = 'CN') {
$phone = preg_replace('/s+/', '', $phone);
// 中国手机号验证
if ($country_code === 'CN') {
if (!preg_match('/^1[3-9]d{9}$/', $phone)) {
$this->add_error($field_name, __('请输入有效的中国手机号码', 'smart-form-validation'));
return false;
}
}
// 国际电话验证(简化版)
else {
if (!preg_match('/^+?[1-9]d{1,14}$/', $phone)) {
$this->add_error($field_name, __('请输入有效的国际电话号码', 'smart-form-validation'));
return false;
}
}
$this->cleaned_data[$field_name] = sanitize_text_field($phone);
return true;
}
/**
* 验证URL
*/
public function validate_url($url, $field_name = 'url') {
if (empty($url)) {
return true; // URL字段可为空
}
// 检查URL格式
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->add_error($field_name, __('请输入有效的URL地址', 'smart-form-validation'));
return false;
}
// 检查URL是否安全
$parsed_url = parse_url($url);
$blacklist_domains = apply_filters('smart_form_url_blacklist', array());
if (in_array($parsed_url['host'], $blacklist_domains)) {
$this->add_error($field_name, __('该URL已被列入黑名单', 'smart-form-validation'));
return false;
}
$this->cleaned_data[$field_name] = esc_url_raw($url);
return true;
}
/**
* 添加错误信息
*/
private function add_error($field, $message) {
$this->errors[$field] = $message;
}
/**
* 获取所有错误
*/
public function get_errors() {
return $this->errors;
}
/**
* 获取清洗后的数据
*/
public function get_cleaned_data() {
return $this->cleaned_data;
}
/**
* 检查是否有错误
*/
public function has_errors() {
return !empty($this->errors);
}
}
除了基础验证,我们还需要实现更智能的验证功能:
/**
* 智能文本内容验证
*/
public function validate_smart_text($text, $field_name, $options = array()) {
$defaults = array(
'min_length' => 0,
'max_length' => 0,
'allow_html' => false,
'block_spam_keywords' => true,
'check_duplicate' => false,
);
$options = wp_parse_args($options, $defaults);
// 检查长度
$text_length = mb_strlen($text, 'UTF-8');
if ($options['min_length'] > 0 && $text_length < $options['min_length']) {
$this->add_error($field_name, sprintf(__('内容至少需要%d个字符', 'smart-form-validation'), $options['min_length']));
return false;
}
if ($options['max_length'] > 0 && $text_length > $options['max_length']) {
$this->add_error($field_name, sprintf(__('内容不能超过%d个字符', 'smart-form-validation'), $options['max_length']));
return false;
}
// 垃圾关键词检测
if ($options['block_spam_keywords']) {
$spam_keywords = $this->get_spam_keywords();
foreach ($spam_keywords as $keyword) {
if (stripos($text, $keyword) !== false) {
$this->add_error($field_name, __('内容包含不被允许的关键词', 'smart-form-validation'));
return false;
}
}
}
// 重复内容检测
if ($options['check_duplicate']) {
$hash = md5($text);
$existing_hashes = get_option('smart_form_submission_hashes', array());
// 清理过期的哈希值(24小时前的提交)
$one_day_ago = time() - 86400;
foreach ($existing_hashes as $timestamp => $content_hash) {
if ($timestamp < $one_day_ago) {
unset($existing_hashes[$timestamp]);
}
}
if (in_array($hash, $existing_hashes)) {
$this->add_error($field_name, __('检测到重复提交的内容', 'smart-form-validation'));
return false;
}
// 存储当前提交的哈希值
$existing_hashes[time()] = $hash;
update_option('smart_form_submission_hashes', $existing_hashes);
}
// 数据清洗
if ($options['allow_html']) {
// 允许有限的HTML标签
$allowed_tags = wp_kses_allowed_html('post');
$cleaned_text = wp_kses($text, $allowed_tags);
} else {
// 完全清除HTML
$cleaned_text = sanitize_textarea_field($text);
}
$this->cleaned_data[$field_name] = $cleaned_text;
return true;
}
/**
* 获取垃圾关键词列表
*/
private function get_spam_keywords() {
$default_keywords = array(
'viagra', 'cialis', 'casino', 'loan', 'mortgage',
'赚钱', '赌博', '色情', '代开发票', '信用卡套现'
);
return apply_filters('smart_form_spam_keywords', $default_keywords);
}
为了提高用户体验,我们可以添加实时验证功能:
/**
* 注册AJAX验证端点
*/
public function register_ajax_handlers() {
add_action('wp_ajax_smart_form_validate_field', array($this, 'ajax_validate_field'));
add_action('wp_ajax_nopriv_smart_form_validate_field', array($this, 'ajax_validate_field'));
}
/**
* AJAX字段验证处理
*/
public function ajax_validate_field() {
// 安全检查
check_ajax_referer('smart_form_validation_nonce', 'nonce');
$field_name = sanitize_text_field($_POST['field_name'] ?? '');
$field_value = $_POST['field_value'] ?? '';
$field_type = sanitize_text_field($_POST['field_type'] ?? 'text');
$validator = new Smart_Form_Validator();
$is_valid = false;
$message = '';
switch ($field_type) {
case 'email':
$is_valid = $validator->validate_email($field_value, $field_name);
break;
case 'phone':
$is_valid = $validator->validate_phone($field_value, $field_name);
break;
case 'url':
$is_valid = $validator->validate_url($field_value, $field_name);
break;
default:
$is_valid = true;
}
if (!$is_valid) {
$errors = $validator->get_errors();
$message = $errors[$field_name] ?? __('字段验证失败', 'smart-form-validation');
}
wp_send_json(array(
'valid' => $is_valid,
'message' => $message,
'cleaned_value' => $validator->get_cleaned_data()[$field_name] ?? $field_value
));
}
数据清洗不仅仅是去除危险字符,还包括标准化和规范化:
/**
* 深度数据清洗类
*/
class Smart_Data_Sanitizer {
/**
* 清洗用户输入数组
*/
public static function sanitize_array($data, $rules = array()) {
$sanitized = array();
foreach ($data as $key => $value) {
$rule = $rules[$key] ?? 'text';
if (is_array($value)) {
$sanitized[$key] = self::sanitize_array($value, $rules);
} else {
$sanitized[$key] = self::sanitize_field($value, $rule);
}
}
return $sanitized;
}
/**
* 根据规则清洗单个字段
*/
public static function sanitize_field($value, $rule = 'text') {
switch ($rule) {
case 'email':
return sanitize_email($value);
case 'url':
return esc_url_raw($value);
case 'textarea':
return sanitize_textarea_field($value);
case 'html':
$allowed_tags = wp_kses_allowed_html('post');
return wp_kses($value, $allowed_tags);
case 'integer':
return intval($value);
case 'float':
return floatval($value);
case 'date':
// 尝试解析日期并格式化为Y-m-d
$timestamp = strtotime($value);
return $timestamp ? date('Y-m-d', $timestamp) : '';
case 'phone':
// 移除所有非数字字符,除了开头的+
$cleaned = preg_replace('/[^d+]/', '', $value);
return substr($cleaned, 0, 20); // 限制长度
case 'credit_card':
// 只保留数字,并添加掩码
$cleaned = preg_replace('/D/', '', $value);
if (strlen($cleaned) >= 4) {
return '**** **** **** ' . substr($cleaned, -4);
}
return $cleaned;
case 'price':
// 格式化价格,保留两位小数
$cleaned = preg_replace('/[^d.]/', '', $value);
$float_value = floatval($cleaned);
return number_format($float_value, 2, '.', '');
default:
return sanitize_text_field($value);
}
}
/**
* 防止SQL注入
*/
public static function prevent_sql_injection($input) {
global $wpdb;
// 使用$wpdb->prepare进行查询参数化
// 这里我们主要进行输入验证
$dangerous_patterns = array(
'/unions+select/i',
'/inserts+into/i',
'/updates+.+set/i',
'/deletes+from/i',
'/drops+table/i',
'/--/',
'/#/',
'//*/',
'/*//',
'/waitfors+delay/i',
'/benchmark(/i'
);
foreach ($dangerous_patterns as $pattern) {
if (preg_match($pattern, $input)) {
return ''; // 发现危险内容,返回空字符串
}
}
return $input;
}
/**
* 防止XSS攻击
*/
public static function prevent_xss($input) {
// 移除危险的HTML属性和事件处理器
$dangerous_attributes = array(
'onload', 'onerror', 'onclick', 'onmouseover',
'onmouseout', 'onkeydown', 'onkeypress', 'onkeyup',
'javascript:', 'vbscript:', 'expression('
);
foreach ($dangerous_attributes as $attr) {
$input = preg_replace('/' . preg_quote($attr, '/') . 's*:/i', 'blocked:', $input);
$input = preg_replace('/' . preg_quote($attr, '/') . 's*=/i', 'blocked=', $input);
}
return $input;
}
}
/**
* 垃圾信息检测类
*/
class Smart_Spam_Detector {
private $score = 0;
private $threshold = 5; // 超过此分数视为垃圾信息
/**
* 检测提交是否为垃圾信息
*/
public function is_spam($data) {
$this->score = 0;
// 检查提交频率
$this->check_submission_frequency();
// 检查隐藏蜜罐字段
$this->check_honeypot($data);
// 检查内容特征
if (isset($data['message'])) {
$this->check_content_features($data['message']);
}
// 检查链接数量
$this->check_link_count($data);
// 检查用户代理
$this->check_user_agent();
// 允许其他插件修改分数
$this->score = apply_filters('smart_form_spam_score', $this->score, $data);
return $this->score >= $this->threshold;
}
/**
* 检查提交频率
*/
private function check_submission_frequency() {
$ip = $this->get_user_ip();
$transient_key = 'smart_form_submission_' . md5($ip);
$submission_count = get_transient($transient_key);
if ($submission_count === false) {
$submission_count = 0;
set_transient($transient_key, 1, 300); // 5分钟限制
} else {
$submission_count++;
set_transient($transient_key, $submission_count, 300);
// 短时间内多次提交增加垃圾分数
if ($submission_count > 3) {
$this->score += ($submission_count - 2) * 2;
}
}
}
/**
* 检查蜜罐字段
*/
private function check_honeypot($data) {
// 蜜罐字段名,对用户不可见
$honeypot_field = apply_filters('smart_form_honeypot_field', 'website_url');
if (isset($data[$honeypot_field]) && !empty($data[$honeypot_field])) {
// 蜜罐字段被填写,很可能是机器人
$this->score += 10;
}
}
/**
* 检查内容特征
*/
private function check_content_features($content) {
// 检查大写字母比例
$total_chars = strlen($content);
$uppercase_chars = preg_match_all('/[A-Z]/', $content);
if ($total_chars > 10) {
$uppercase_ratio = $uppercase_chars / $total_chars;
if ($uppercase_ratio > 0.5) {
$this->score += 3; // 过多大写字母
}
}
// 检查垃圾关键词
$spam_keywords = array(
'viagra', 'cialis', 'casino', 'loan', 'mortgage',
'赚钱', '赌博', '色情', '代开发票', '信用卡套现'
);
foreach ($spam_keywords as $keyword) {
if (stripos($content, $keyword) !== false) {
$this->score += 5;
break;
}
}
// 检查无意义重复
if ($this->has_repetitive_pattern($content)) {
$this->score += 4;
}
}
/**
* 检查链接数量
*/
private function check_link_count($data) {
$total_links = 0;
foreach ($data as $value) {
if (is_string($value)) {
// 简单统计链接数量
$links = preg_match_all('/https?://[^s]+/', $value, $matches);
$total_links += $links;
}
}
if ($total_links > 2) {
$this->score += $total_links; // 每个链接加1分
}
}
/**
* 检查用户代理
*/
private function check_user_agent() {
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (empty($user_agent)) {
$this->score += 3; // 空用户代理
return;
}
// 已知的垃圾机器人用户代理
$spam_bots = array(
'bot', 'crawler', 'spider', 'scraper',
'curl', 'wget', 'python-requests', 'java'
);
foreach ($spam_bots as $bot) {
if (stripos($user_agent, $bot) !== false) {
$this->score += 2;
break;
}
}
}
/**
* 获取用户IP
*/
private function get_user_ip() {
$ip = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
}
return sanitize_text_field($ip);
}
/**
* 检查是否有重复模式
*/
private function has_repetitive_pattern($content) {
// 检查连续重复的单词
if (preg_match('/(bw+b)(?:s+1){2,}/i', $content)) {
return true;
}
// 检查重复字符
if (preg_match('/(.)1{5,}/', $content)) {
return true;
}
return false;
}
}
/**
* 垃圾信息检测类
*/
class Smart_Spam_Detector {
private $score = 0;
private $threshold = 5; // 超过此分数视为垃圾信息
/**
* 检测提交是否为垃圾信息
*/
public function is_spam($data) {
$this->score = 0;
// 检查提交频率
$this->check_submission_frequency();
// 检查隐藏蜜罐字段
$this->check_honeypot($data);
// 检查内容特征
if (isset($data['message'])) {
$this->check_content_features($data['message']);
}
// 检查链接数量
$this->check_link_count($data);
// 检查用户代理
$this->check_user_agent();
// 允许其他插件修改分数
$this->score = apply_filters('smart_form_spam_score', $this->score, $data);
return $this->score >= $this->threshold;
}
/**
* 检查提交频率
*/
private function check_submission_frequency() {
$ip = $this->get_user_ip();
$transient_key = 'smart_form_submission_' . md5($ip);
$submission_count = get_transient($transient_key);
if ($submission_count === false) {
$submission_count = 0;
set_transient($transient_key, 1, 300); // 5分钟限制
} else {
$submission_count++;
set_transient($transient_key, $submission_count, 300);
// 短时间内多次提交增加垃圾分数
if ($submission_count > 3) {
$this->score += ($submission_count - 2) * 2;
}
}
}
/**
* 检查蜜罐字段
*/
private function check_honeypot($data) {
// 蜜罐字段名,对用户不可见
$honeypot_field = apply_filters('smart_form_honeypot_field', 'website_url');
if (isset($data[$honeypot_field]) && !empty($data[$honeypot_field])) {
// 蜜罐字段被填写,很可能是机器人
$this->score += 10;
}
}
/**
* 检查内容特征
*/
private function check_content_features($content) {
// 检查大写字母比例
$total_chars = strlen($content);
$uppercase_chars = preg_match_all('/[A-Z]/', $content);
if ($total_chars > 10) {
$uppercase_ratio = $uppercase_chars / $total_chars;
if ($uppercase_ratio > 0.5) {
$this->score += 3; // 过多大写字母
}
}
// 检查垃圾关键词
$spam_keywords = array(
'viagra', 'cialis', 'casino', 'loan', 'mortgage',
'赚钱', '赌博', '色情', '代开发票', '信用卡套现'
);
foreach ($spam_keywords as $keyword) {
if (stripos($content, $keyword) !== false) {
$this->score += 5;
break;
}
}
// 检查无意义重复
if ($this->has_repetitive_pattern($content)) {
$this->score += 4;
}
}
/**
* 检查链接数量
*/
private function check_link_count($data) {
$total_links = 0;
foreach ($data as $value) {
if (is_string($value)) {
// 简单统计链接数量
$links = preg_match_all('/https?://[^s]+/', $value, $matches);
$total_links += $links;
}
}
if ($total_links > 2) {
$this->score += $total_links; // 每个链接加1分
}
}
/**
* 检查用户代理
*/
private function check_user_agent() {
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (empty($user_agent)) {
$this->score += 3; // 空用户代理
return;
}
// 已知的垃圾机器人用户代理
$spam_bots = array(
'bot', 'crawler', 'spider', 'scraper',
'curl', 'wget', 'python-requests', 'java'
);
foreach ($spam_bots as $bot) {
if (stripos($user_agent, $bot) !== false) {
$this->score += 2;
break;
}
}
}
/**
* 获取用户IP
*/
private function get_user_ip() {
$ip = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
}
return sanitize_text_field($ip);
}
/**
* 检查是否有重复模式
*/
private function has_repetitive_pattern($content) {
// 检查连续重复的单词
if (preg_match('/(bw+b)(?:s+1){2,}/i', $content)) {
return true;
}
// 检查重复字符
if (preg_match('/(.)1{5,}/', $content)) {
return true;
}
return false;
}
}
/**
* 地址智能处理类
*/
class Smart_Address_Processor {
/**
* 集成第三方地址API进行验证
*/
public static function validate_address($address, $country = 'CN') {
$result = array(
'valid' => false,
'normalized' => '',
'components' => array(),
'suggestions' => array()
);
// 基础格式验证
if (empty($address) || strlen($address) < 5) {
return $result;
}
// 使用百度地图API进行地址验证(示例)
if ($country === 'CN' && defined('SMART_FORM_BAIDU_MAP_AK')) {
$api_result = self::validate_via_baidu_map($address);
if ($api_result) {
return $api_result;
}
}
// 本地规则验证
return self::validate_locally($address, $country);
}
/**
* 通过百度地图API验证地址
*/
private static function validate_via_baidu_map($address) {
$api_key = SMART_FORM_BAIDU_MAP_AK;
$url = "http://api.map.baidu.com/geocoding/v3/?address=" .
urlencode($address) . "&output=json&ak=" . $api_key;
$response = wp_remote_get($url, array('timeout' => 5));
if (is_wp_error($response)) {
return false;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if ($data && $data['status'] == 0) {
return array(
'valid' => true,
'normalized' => $data['result']['formatted_address'] ?? $address,
'components' => array(
'province' => $data['result']['addressComponent']['province'] ?? '',
'city' => $data['result']['addressComponent']['city'] ?? '',
'district' => $data['result']['addressComponent']['district'] ?? '',
'street' => $data['result']['addressComponent']['street'] ?? ''
),
'location' => array(
'lng' => $data['result']['location']['lng'] ?? '',
'lat' => $data['result']['location']['lat'] ?? ''
)
);
}
return false;
}
/**
* 本地地址验证规则
*/
private static function validate_locally($address, $country) {
$result = array(
'valid' => false,
'normalized' => $address,
'components' => array()
);
// 中国地址验证规则
if ($country === 'CN') {
// 检查是否包含必要的地址元素
$required_elements = array('省', '市', '区', '路', '街', '号');
$found_elements = 0;
foreach ($required_elements as $element) {
if (mb_strpos($address, $element) !== false) {
$found_elements++;
}
}
$result['valid'] = $found_elements >= 2;
// 尝试提取地址组件
if (preg_match('/(.*?[省市])(.*?[市区县])(.*?[路街道])(.*)/', $address, $matches)) {
$result['components'] = array(
'province' => $matches[1] ?? '',
'city' => $matches[2] ?? '',
'district' => $matches[3] ?? '',
'detail' => $matches[4] ?? ''
);
}
}
return $result;
}
/**
* 地址自动补全
*/
public static function autocomplete_address($input, $country = 'CN') {
$suggestions = array();
// 这里可以集成第三方地址补全API
// 示例:使用本地地址数据库
$address_database = get_option('smart_form_address_db', array());
if (!empty($address_database)) {
foreach ($address_database as $address) {
if (stripos($address, $input) !== false) {
$suggestions[] = $address;
if (count($suggestions) >= 5) {
break;
}
}
}
}
return apply_filters('smart_form_address_suggestions', $suggestions, $input, $country);
}
}
/**
* 地址智能处理类
*/
class Smart_Address_Processor {
/**
* 集成第三方地址API进行验证
*/
public static function validate_address($address, $country = 'CN') {
$result = array(
'valid' => false,
'normalized' => '',
'components' => array(),
'suggestions' => array()
);
// 基础格式验证
if (empty($address) || strlen($address) < 5) {
return $result;
}
// 使用百度地图API进行地址验证(示例)
if ($country === 'CN' && defined('SMART_FORM_BAIDU_MAP_AK')) {
$api_result = self::validate_via_baidu_map($address);
if ($api_result) {
return $api_result;
}
}
// 本地规则验证
return self::validate_locally($address, $country);
}
/**
* 通过百度地图API验证地址
*/
private static function validate_via_baidu_map($address) {
$api_key = SMART_FORM_BAIDU_MAP_AK;
$url = "http://api.map.baidu.com/geocoding/v3/?address=" .
urlencode($address) . "&output=json&ak=" . $api_key;
$response = wp_remote_get($url, array('timeout' => 5));
if (is_wp_error($response)) {
return false;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if ($data && $data['status'] == 0) {
return array(
'valid' => true,
'normalized' => $data['result']['formatted_address'] ?? $address,
'components' => array(
'province' => $data['result']['addressComponent']['province'] ?? '',
'city' => $data['result']['addressComponent']['city'] ?? '',
'district' => $data['result']['addressComponent']['district'] ?? '',
'street' => $data['result']['addressComponent']['street'] ?? ''
),
'location' => array(
'lng' => $data['result']['location']['lng'] ?? '',
'lat' => $data['result']['location']['lat'] ?? ''
)
);
}
return false;
}
/**
* 本地地址验证规则
*/
private static function validate_locally($address, $country) {
$result = array(
'valid' => false,
'normalized' => $address,
'components' => array()
);
// 中国地址验证规则
if ($country === 'CN') {
// 检查是否包含必要的地址元素
$required_elements = array('省', '市', '区', '路', '街', '号');
$found_elements = 0;
foreach ($required_elements as $element) {
if (mb_strpos($address, $element) !== false) {
$found_elements++;
}
}
$result['valid'] = $found_elements >= 2;
// 尝试提取地址组件
if (preg_match('/(.*?[省市])(.*?[市区县])(.*?[路街道])(.*)/', $address, $matches)) {
$result['components'] = array(
'province' => $matches[1] ?? '',
'city' => $matches[2] ?? '',
'district' => $matches[3] ?? '',
'detail' => $matches[4] ?? ''
);
}
}
return $result;
}
/**
* 地址自动补全
*/
public static function autocomplete_address($input, $country = 'CN') {
$suggestions = array();
// 这里可以集成第三方地址补全API
// 示例:使用本地地址数据库
$address_database = get_option('smart_form_address_db', array());
if (!empty($address_database)) {
foreach ($address_database as $address) {
if (stripos($address, $input) !== false) {
$suggestions[] = $address;
if (count($suggestions) >= 5) {
break;
}
}
}
}
return apply_filters('smart_form_address_suggestions', $suggestions, $input, $country);
}
}
/**
* 身份证验证类
*/
class Smart_ID_Validator {
/**
* 验证中国身份证号码
*/
public static function validate_chinese_id($id_number) {
$id_number = strtoupper(trim($id_number));
// 基本格式验证
if (!preg_match('/^d{17}[dX]$/', $id_number)) {
return array(
'valid' => false,
'error' => '身份证格式不正确',
'details' => array()
);
}
// 验证校验码
if (!self::verify_check_code($id_number)) {
return array(
'valid' => false,
'error' => '身份证校验码错误',
'details' => array()
);
}
// 提取信息
$details = self::extract_id_info($id_number);
// 验证出生日期
if (!checkdate($details['month'], $details['day'], $details['year'])) {
return array(
'valid' => false,
'error' => '身份证出生日期无效',
'details' => $details
);
}
// 验证地区代码(前6位)
if (!self::validate_region_code(substr($id_number, 0, 6))) {
return array(
'valid' => false,
'error' => '身份证地区代码无效',
'details' => $details
);
}
return array(
'valid' => true,
'error' => '',
'details' => $details
);
}
/**
* 验证校验码
*/
private static function verify_check_code($id_number) {
if (strlen($id_number) != 18) {
return false;
}
// 加权因子
$weight_factors = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
// 校验码对应值
$check_codes = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
$sum = 0;
for ($i = 0; $i < 17; $i++) {
$sum += intval($id_number[$i]) * $weight_factors[$i];
}
$mod = $sum % 11;
$check_code = $check_codes[$mod];
return $check_code == $id_number[17];
}
/**
* 从身份证提取信息
*/
private static function extract_id_info($id_number) {
// 地区代码
$region_code = substr($id_number, 0, 6);
// 出生日期
$birth_year = substr($id_number, 6, 4);
$birth_month = substr($id_number, 10, 2);
$birth_day = substr($id_number, 12, 2);
// 顺序码
$sequence_code = substr($id_number, 14, 3);
// 性别(顺序码奇数为男,偶数为女)
$gender_code = intval($sequence_code);
$gender = ($gender_code % 2 == 1) ? '男' : '女';
// 年龄计算
$current_year = date('Y');
$age = $current_year - $birth_year;
// 判断是否已过生日
$current_month = date('m');
$current_day = date('d');
if ($current_month < $birth_month ||
($current_month == $birth_month && $current_day < $birth_day)) {
$age--;
}
return array(
'region_code' => $region_code,
'birth_date' => $birth_year . '-' . $birth_month . '-' . $birth_day,
'year' => intval($birth_year),
'month' => intval($birth_month),
'day' => intval($birth_day),
'sequence_code' => $sequence_code,
'gender' => $gender,
'age' => $age
);
}
/**
* 验证地区代码
*/
private static function validate_region_code($region_code) {
// 这里可以集成地区代码数据库
// 简化版:只验证基本格式
$valid_prefixes = array(
'11', '12', '13', '14', '15', // 华北
'21', '22', '23', // 东北
'31', '32', '33', '34', // 华东
'35', '36', '37', // 华中
'41', '42', '43', '44', '45', '46', // 华南
'50', '51', '52', '53', '54', // 西南
'61', '62', '63', '64', '65', // 西北
'71', '81', '82' // 港澳台
);
$prefix = substr($region_code, 0, 2);
return in_array($prefix, $valid_prefixes);
}
}
/**
* 身份证验证类
*/
class Smart_ID_Validator {
/**
* 验证中国身份证号码
*/
public static function validate_chinese_id($id_number) {
$id_number = strtoupper(trim($id_number));
// 基本格式验证
if (!preg_match('/^d{17}[dX]$/', $id_number)) {
return array(
'valid' => false,
'error' => '身份证格式不正确',
'details' => array()
);
}
// 验证校验码
if (!self::verify_check_code($id_number)) {
return array(
'valid' => false,
'error' => '身份证校验码错误',
'details' => array()
);
}
// 提取信息
$details = self::extract_id_info($id_number);
// 验证出生日期
if (!checkdate($details['month'], $details['day'], $details['year'])) {
return array(
'valid' => false,
'error' => '身份证出生日期无效',
'details' => $details
);
}
// 验证地区代码(前6位)
if (!self::validate_region_code(substr($id_number, 0, 6))) {
return array(
'valid' => false,
'error' => '身份证地区代码无效',
'details' => $details
);
}
return array(
'valid' => true,
'error' => '',
'details' => $details
);
}
/**
* 验证校验码
*/
private static function verify_check_code($id_number) {
if (strlen($id_number) != 18) {
return false;
}
// 加权因子
$weight_factors = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
// 校验码对应值
$check_codes = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
$sum = 0;
for ($i = 0; $i < 17; $i++) {
$sum += intval($id_number[$i]) * $weight_factors[$i];
}
$mod = $sum % 11;
$check_code = $check_codes[$mod];
return $check_code == $id_number[17];
}
/**
* 从身份证提取信息
*/
private static function extract_id_info($id_number) {
// 地区代码
$region_code = substr($id_number, 0, 6);
// 出生日期
$birth_year = substr($id_number, 6, 4);
$birth_month = substr($id_number, 10, 2);
$birth_day = substr($id_number, 12, 2);
// 顺序码
$sequence_code = substr($id_number, 14, 3);
// 性别(顺序码奇数为男,偶数为女)
$gender_code = intval($sequence_code);
$gender = ($gender_code % 2 == 1) ? '男' : '女';
// 年龄计算
$current_year = date('Y');
$age = $current_year - $birth_year;
// 判断是否已过生日
$current_month = date('m');
$current_day = date('d');
if ($current_month < $birth_month ||
($current_month == $birth_month && $current_day < $birth_day)) {
$age--;
}
return array(
'region_code' => $region_code,
'birth_date' => $birth_year . '-' . $birth_month . '-' . $birth_day,
'year' => intval($birth_year),
'month' => intval($birth_month),
'day' => intval($birth_day),
'sequence_code' => $sequence_code,
'gender' => $gender,
'age' => $age
);
}
/**
* 验证地区代码
*/
private static function validate_region_code($region_code) {
// 这里可以集成地区代码数据库
// 简化版:只验证基本格式
$valid_prefixes = array(
'11', '12', '13', '14', '15', // 华北
'21', '22', '23', // 东北
'31', '32', '33', '34', // 华东
'35', '36', '37', // 华中
'41', '42', '43', '44', '45', '46', // 华南
'50', '51', '52', '53', '54', // 西南
'61', '62', '63', '64', '65', // 西北
'71', '81', '82' // 港澳台
);
$prefix = substr($region_code, 0, 2);
return in_array($prefix, $valid_prefixes);
}
}
/**
* 银行卡验证类
*/
class Smart_Bankcard_Validator {
/**
* 验证银行卡号
*/
public static function validate_bankcard($card_number) {
$card_number = preg_replace('/s+/', '', $card_number);
// 基本格式验证
if (!preg_match('/^d{13,19}$/', $card_number)) {
return array(
'valid' => false,
'error' => '银行卡号格式不正确',
'details' => array()
);
}
// Luhn算法验证
if (!self::validate_luhn($card_number)) {
return array(
'valid' => false,
'error' => '银行卡号校验失败',
'details' => array()
);
}
// 识别银行和卡类型
$details = self::identify_bankcard($card_number);
return array(
'valid' => true,
'error' => '',
'details' => $details
);
}
/**
* Luhn算法验证
*/
private static function validate_luhn($card_number) {
$sum = 0;
$length = strlen($card_number);
$parity = $length % 2;
for ($i = 0; $i < $length; $i++) {
$digit = intval($card_number[$i]);
if ($i % 2 == $parity) {
$digit *= 2;
if ($digit > 9) {
$digit -= 9;
}
}
$sum += $digit;
}
return ($sum % 10) == 0;
}
/**
* 识别银行卡信息
*/
private static function identify_bankcard($card_number) {
$bin_codes = array(
// 借记卡
'622848' => array('bank' => '农业银行', 'type' => '借记卡'),
'622700' => array('bank' => '建设银行', 'type' => '借记卡'),
'622262' => array('bank' => '交通银行', 'type' => '借记卡'),
'622588' => array('bank' => '招商银行', 'type' => '借记卡'),
'622760' => array('bank' => '中国银行', 'type' => '借记卡'),
'622202' => array('bank' => '工商银行', 'type' => '借记卡'),
// 信用卡
'438088' => array('bank' => '建设银行', 'type' => '信用卡'),
'518710' => array('bank' => '招商银行', 'type' => '信用卡'),
'622155' => array('bank' => '工商银行', 'type' => '信用卡'),
'622156' => array('bank' => '工商银行', 'type' => '信用卡'),
// 更多BIN码可以继续添加
);
$details = array(
'bank' => '未知',
'type' => '未知',
'length' => strlen($card_number),
'bin' => substr($card_number, 0, 6)
);
// 检查前6位BIN码
foreach ($bin_codes as $bin => $info) {
/**
* 银行卡验证类
*/
class Smart_Bankcard_Validator {
/**
* 验证银行卡号
*/
public static function validate_bankcard($card_number) {
$card_number = preg_replace('/s+/', '', $card_number);
// 基本格式验证
if (!preg_match('/^d{13,19}$/', $card_number)) {
return array(
'valid' => false,
'error' => '银行卡号格式不正确',
'details' => array()
);
}
// Luhn算法验证
if (!self::validate_luhn($card_number)) {
return array(
'valid' => false,
'error' => '银行卡号校验失败',
'details' => array()
);
}
// 识别银行和卡类型
$details = self::identify_bankcard($card_number);
return array(
'valid' => true,
'error' => '',
'details' => $details
);
}
/**
* Luhn算法验证
*/
private static function validate_luhn($card_number) {
$sum = 0;
$length = strlen($card_number);
$parity = $length % 2;
for ($i = 0; $i < $length; $i++) {
$digit = intval($card_number[$i]);
if ($i % 2 == $parity) {
$digit *= 2;
if ($digit > 9) {
$digit -= 9;
}
}
$sum += $digit;
}
return ($sum % 10) == 0;
}
/**
* 识别银行卡信息
*/
private static function identify_bankcard($card_number) {
$bin_codes = array(
// 借记卡
'622848' => array('bank' => '农业银行', 'type' => '借记卡'),
'622700' => array('bank' => '建设银行', 'type' => '借记卡'),
'622262' => array('bank' => '交通银行', 'type' => '借记卡'),
'622588' => array('bank' => '招商银行', 'type' => '借记卡'),
'622760' => array('bank' => '中国银行', 'type' => '借记卡'),
'622202' => array('bank' => '工商银行', 'type' => '借记卡'),
// 信用卡
'438088' => array('bank' => '建设银行', 'type' => '信用卡'),
'518710' => array('bank' => '招商银行', 'type' => '信用卡'),
'622155' => array('bank' => '工商银行', 'type' => '信用卡'),
'622156' => array('bank' => '工商银行', 'type' => '信用卡'),
// 更多BIN码可以继续添加
);
$details = array(
'bank' => '未知',
'type' => '未知',
'length' => strlen($card_number),
'bin' => substr($card_number, 0, 6)
);
// 检查前6位BIN码
foreach ($bin_codes as $bin => $info) {


