PHP 自定义filter_input_validate()函数

注意:filter_input_validate() 不是PHP内置函数,而是本教程提供的自定义验证函数,用于增强PHP的输入验证功能。

定义和用法

filter_input_validate() 是一个自定义的高级输入验证函数,它扩展了PHP内置的 filter_input() 函数,提供了更丰富的验证规则和错误处理机制。

该函数提供了以下增强功能:

  • 支持自定义验证规则
  • 详细的错误信息返回
  • 批量验证多个字段
  • 支持条件验证
  • 灵活的验证规则配置

自定义函数实现

<?php
/**
 * 高级输入验证函数
 *
 * @param int $type 输入类型 (INPUT_GET, INPUT_POST, INPUT_COOKIE, etc.)
 * @param string $field 字段名
 * @param mixed $rules 验证规则(字符串或数组)
 * @param mixed $default 验证失败时的默认值
 * @return array 包含验证结果和信息的数组
 */
function filter_input_validate($type, $field, $rules, $default = null) {
    // 获取原始值
    $value = filter_input($type, $field);

    // 如果字段不存在,使用默认值
    if ($value === null) {
        $value = $default;
    }

    // 如果值为null且没有默认值,返回验证失败
    if ($value === null) {
        return [
            'valid' => false,
            'value' => null,
            'error' => '字段不存在',
            'field' => $field
        ];
    }

    // 将规则字符串转换为数组
    if (is_string($rules)) {
        $rules = explode('|', $rules);
    }

    $errors = [];
    $validatedValue = $value;

    // 应用每个验证规则
    foreach ($rules as $rule) {
        $ruleParts = explode(':', $rule, 2);
        $ruleName = $ruleParts[0];
        $ruleParam = isset($ruleParts[1]) ? $ruleParts[1] : null;

        switch ($ruleName) {
            case 'required':
                if (empty(trim($validatedValue))) {
                    $errors[] = '字段是必填的';
                }
                break;

            case 'email':
                if (!filter_var($validatedValue, FILTER_VALIDATE_EMAIL)) {
                    $errors[] = '邮箱格式无效';
                }
                break;

            case 'url':
                if (!filter_var($validatedValue, FILTER_VALIDATE_URL)) {
                    $errors[] = 'URL格式无效';
                }
                break;

            case 'int':
                $options = [];
                if ($ruleParam) {
                    $range = explode(',', $ruleParam);
                    if (count($range) >= 2) {
                        $options['options'] = [
                            'min_range' => (int)$range[0],
                            'max_range' => (int)$range[1]
                        ];
                    }
                }
                $filtered = filter_var($validatedValue, FILTER_VALIDATE_INT, $options);
                if ($filtered === false) {
                    $errors[] = $ruleParam ? "整数必须在{$range[0]}-{$range[1]}之间" : '必须是有效的整数';
                } else {
                    $validatedValue = $filtered;
                }
                break;

            case 'float':
                $filtered = filter_var($validatedValue, FILTER_VALIDATE_FLOAT);
                if ($filtered === false) {
                    $errors[] = '必须是有效的浮点数';
                } else {
                    $validatedValue = $filtered;
                }
                break;

            case 'boolean':
                $filtered = filter_var($validatedValue, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                if ($filtered === null) {
                    $errors[] = '必须是有效的布尔值';
                } else {
                    $validatedValue = $filtered;
                }
                break;

            case 'ip':
                $filtered = filter_var($validatedValue, FILTER_VALIDATE_IP);
                if ($filtered === false) {
                    $errors[] = '必须是有效的IP地址';
                } else {
                    $validatedValue = $filtered;
                }
                break;

            case 'min':
                if (strlen($validatedValue) < (int)$ruleParam) {
                    $errors[] = "长度不能少于{$ruleParam}个字符";
                }
                break;

            case 'max':
                if (strlen($validatedValue) > (int)$ruleParam) {
                    $errors[] = "长度不能超过{$ruleParam}个字符";
                }
                break;

            case 'regex':
                if (!preg_match($ruleParam, $validatedValue)) {
                    $errors[] = '格式不符合要求';
                }
                break;

            case 'alpha':
                if (!ctype_alpha(str_replace(' ', '', $validatedValue))) {
                    $errors[] = '只能包含字母';
                }
                break;

            case 'alnum':
                if (!ctype_alnum(str_replace(' ', '', $validatedValue))) {
                    $errors[] = '只能包含字母和数字';
                }
                break;

            case 'numeric':
                if (!is_numeric($validatedValue)) {
                    $errors[] = '必须是数字';
                }
                break;

            case 'date':
                if (!strtotime($validatedValue)) {
                    $errors[] = '必须是有效的日期';
                }
                break;

            case 'in':
                $allowed = explode(',', $ruleParam);
                if (!in_array($validatedValue, $allowed)) {
                    $errors[] = "值必须是: " . implode(', ', $allowed);
                }
                break;

            case 'not_in':
                $disallowed = explode(',', $ruleParam);
                if (in_array($validatedValue, $disallowed)) {
                    $errors[] = "值不能是: " . implode(', ', $disallowed);
                }
                break;

            case 'same':
                $otherValue = filter_input($type, $ruleParam);
                if ($validatedValue !== $otherValue) {
                    $errors[] = "必须与{$ruleParam}字段相同";
                }
                break;

            case 'different':
                $otherValue = filter_input($type, $ruleParam);
                if ($validatedValue === $otherValue) {
                    $errors[] = "必须与{$ruleParam}字段不同";
                }
                break;

            case 'callback':
                if (is_callable($ruleParam) && !call_user_func($ruleParam, $validatedValue)) {
                    $errors[] = '自定义验证失败';
                }
                break;

            default:
                // 默认使用内置过滤器
                $filterId = filter_id($ruleName);
                if ($filterId !== false) {
                    $filtered = filter_var($validatedValue, $filterId);
                    if ($filtered === false) {
                        $errors[] = "{$ruleName}验证失败";
                    } else {
                        $validatedValue = $filtered;
                    }
                }
                break;
        }

        // 如果已经有错误,停止后续验证
        if (!empty($errors)) {
            break;
        }
    }

    // 返回验证结果
    return [
        'valid' => empty($errors),
        'value' => $validatedValue,
        'error' => empty($errors) ? null : implode('; ', $errors),
        'field' => $field,
        'errors' => $errors
    ];
}
?>

函数参数

参数 描述
$type 输入类型。与 filter_input() 相同:
  • INPUT_GET
  • INPUT_POST
  • INPUT_COOKIE
  • INPUT_SERVER
  • INPUT_ENV
$field 字段名。要验证的字段名称
$rules

验证规则。可以是字符串或数组:

  • 字符串:用竖线(|)分隔的规则,如 "required|email|max:100"
  • 数组:规则数组,如 ['required', 'email', 'max:100']
$default 默认值(可选)。如果字段不存在,使用此默认值

支持的验证规则

规则 格式 描述 示例
required - 字段必须存在且不为空 required
email - 必须是有效的邮箱地址 email
url - 必须是有效的URL url
int int:min,max 必须是整数(可指定范围) int:1,100
float - 必须是浮点数 float
boolean - 必须是布尔值 boolean
ip - 必须是有效的IP地址 ip
min min:length 最小长度限制 min:3
max max:length 最大长度限制 max:100
regex regex:pattern 必须匹配正则表达式 regex:/^[a-z]+$/
alpha - 只能包含字母 alpha
alnum - 只能包含字母和数字 alnum
numeric - 必须是数字 numeric
date - 必须是有效的日期 date
in in:value1,value2 必须在指定值列表中 in:admin,user,guest
not_in not_in:value1,value2 不能在指定值列表中 not_in:admin,root
same same:field_name 必须与另一个字段值相同 same:password_confirmation
different different:field_name 必须与另一个字段值不同 different:old_password
callback callback:function_name 使用自定义函数验证 callback:my_validation_function

返回值

函数返回一个包含以下键的关联数组:

[
    'valid'   => true,      // 布尔值:验证是否通过
    'value'   => mixed,     // 验证后的值(已转换类型)
    'error'   => null,      // 字符串:错误信息(验证失败时)
    'field'   => string,    // 字段名
    'errors'  => array      // 详细的错误数组
]

示例

示例 1:基本验证使用

<?php
// 包含上面的自定义函数定义
require_once 'filter_input_validate.php';

// 模拟POST数据
$_POST = [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'age' => '25',
    'website' => 'https://example.com'
];

// 验证单个字段
$nameResult = filter_input_validate(INPUT_POST, 'name', 'required|alpha|min:2|max:50');
$emailResult = filter_input_validate(INPUT_POST, 'email', 'required|email');
$ageResult = filter_input_validate(INPUT_POST, 'age', 'required|int:18,100');
$websiteResult = filter_input_validate(INPUT_POST, 'website', 'url');

// 显示验证结果
echo "<h4>验证结果:</h4>";
echo "<table class='table table-bordered'>";
echo "<tr><th>字段</th><th>值</th><th>状态</th><th>错误信息</th></tr>";

$results = [$nameResult, $emailResult, $ageResult, $websiteResult];
foreach ($results as $result) {
    $status = $result['valid'] ? '<span class="badge bg-success">通过</span>' :
                                 '<span class="badge bg-danger">失败</span>';
    $error = $result['error'] ?? '无';

    echo "<tr>";
    echo "<td>{$result['field']}</td>";
    echo "<td>" . htmlspecialchars($result['value']) . "</td>";
    echo "<td>$status</td>";
    echo "<td>" . htmlspecialchars($error) . "</td>";
    echo "</tr>";
}

echo "</table>";
?>

示例 2:用户注册表单验证

<?php
// 用户注册表单处理
function validateRegistrationForm() {
    // 定义验证规则
    $rules = [
        'username' => 'required|alnum|min:3|max:20',
        'email' => 'required|email',
        'password' => 'required|min:8|regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/',
        'confirm_password' => 'required|same:password',
        'age' => 'required|int:18,120',
        'gender' => 'in:male,female,other',
        'terms' => 'required|boolean',
        'newsletter' => 'boolean'
    ];

    $errors = [];
    $validData = [];

    // 验证每个字段
    foreach ($rules as $field => $fieldRules) {
        $result = filter_input_validate(INPUT_POST, $field, $fieldRules);

        if (!$result['valid']) {
            $errors[$field] = $result['error'];
        } else {
            $validData[$field] = $result['value'];
        }
    }

    // 返回验证结果
    if (empty($errors)) {
        return [
            'success' => true,
            'message' => '验证成功',
            'data' => $validData
        ];
    } else {
        return [
            'success' => false,
            'message' => '验证失败',
            'errors' => $errors,
            'data' => $validData
        ];
    }
}

// 模拟表单提交
$_POST = [
    'username' => 'john_doe123',
    'email' => 'john.doe@example.com',
    'password' => 'Password123',
    'confirm_password' => 'Password123',
    'age' => '25',
    'gender' => 'male',
    'terms' => 'on',
    'newsletter' => '1'
];

// 执行验证
$result = validateRegistrationForm();

// 显示结果
echo "<div class='" . ($result['success'] ? 'alert alert-success' : 'alert alert-danger') . "'>";
echo "<h4>{$result['message']}</h4>";

if (!$result['success']) {
    echo "<ul>";
    foreach ($result['errors'] as $field => $error) {
        echo "<li><strong>$field</strong>: $error</li>";
    }
    echo "</ul>";
} else {
    echo "<pre>" . print_r($result['data'], true) . "</pre>";
}
echo "</div>";
?>

示例 3:批量验证函数

<?php
/**
 * 批量验证多个字段
 */
function filter_input_validate_batch($type, $fields) {
    $results = [];
    $allValid = true;

    foreach ($fields as $field => $config) {
        $rules = $config['rules'] ?? '';
        $default = $config['default'] ?? null;

        $result = filter_input_validate($type, $field, $rules, $default);
        $results[$field] = $result;

        if (!$result['valid']) {
            $allValid = false;
        }
    }

    return [
        'valid' => $allValid,
        'results' => $results
    ];
}

// 使用批量验证
$validationConfig = [
    'username' => [
        'rules' => 'required|alnum|min:3|max:20',
        'default' => 'guest'
    ],
    'email' => [
        'rules' => 'required|email'
    ],
    'age' => [
        'rules' => 'int:0,150'
    ],
    'score' => [
        'rules' => 'float'
    ],
    'ip_address' => [
        'rules' => 'ip'
    ]
];

// 模拟GET数据
$_GET = [
    'username' => 'test_user',
    'email' => 'test@example.com',
    'age' => '30',
    'score' => '95.5',
    'ip_address' => '192.168.1.1'
];

// 执行批量验证
$batchResult = filter_input_validate_batch(INPUT_GET, $validationConfig);

echo "<h4>批量验证结果:</h4>";
echo "<div class='" . ($batchResult['valid'] ? 'alert alert-success' : 'alert alert-danger') . "'>";
echo "总体验证状态: " . ($batchResult['valid'] ? '通过' : '失败');
echo "</div>";

echo "<table class='table table-bordered'>";
echo "<thead><tr><th>字段</th><th>值</th><th>规则</th><th>状态</th><th>错误</th></tr></thead>";
echo "<tbody>";

foreach ($batchResult['results'] as $field => $result) {
    $status = $result['valid'] ? '<span class="badge bg-success">✓</span>' :
                                 '<span class="badge bg-danger">✗</span>';
    $error = $result['error'] ?? '无';
    $rules = $validationConfig[$field]['rules'] ?? '无';

    echo "<tr>";
    echo "<td>$field</td>";
    echo "<td>" . htmlspecialchars($result['value']) . "</td>";
    echo "<td>$rules</td>";
    echo "<td>$status</td>";
    echo "<td>" . htmlspecialchars($error) . "</td>";
    echo "</tr>";
}

echo "</tbody></table>";
?>

示例 4:自定义验证规则

<?php
// 自定义验证函数
function validate_phone_number($value) {
    // 验证电话号码格式
    return preg_match('/^\+?[0-9]{10,15}$/', $value);
}

function validate_strong_password($value) {
    // 验证强密码:至少8位,包含大小写字母和数字
    if (strlen($value) < 8) return false;
    if (!preg_match('/[A-Z]/', $value)) return false;
    if (!preg_match('/[a-z]/', $value)) return false;
    if (!preg_match('/[0-9]/', $value)) return false;
    return true;
}

// 使用自定义回调验证
$_POST = [
    'phone' => '+1234567890',
    'password' => 'MyP@ssw0rd123',
    'custom_field' => 'special_value'
];

// 验证电话号码
$phoneResult = filter_input_validate(INPUT_POST, 'phone',
    'required|callback:validate_phone_number');

// 验证密码强度
$passwordResult = filter_input_validate(INPUT_POST, 'password',
    'required|callback:validate_strong_password');

// 使用匿名函数验证
$customResult = filter_input_validate(INPUT_POST, 'custom_field',
    'callback:function($v){ return $v === "special_value"; }');

echo "<h4>自定义规则验证:</h4>";
$results = [$phoneResult, $passwordResult, $customResult];
foreach ($results as $result) {
    echo "<div class='mb-2 p-2 " . ($result['valid'] ? 'bg-success' : 'bg-danger') . "'>";
    echo "字段: {$result['field']}<br>";
    echo "值: " . htmlspecialchars($result['value']) . "<br>";
    echo "状态: " . ($result['valid'] ? '通过' : '失败') . "<br>";
    if (!$result['valid']) {
        echo "错误: " . htmlspecialchars($result['error']) . "<br>";
    }
    echo "</div>";
}
?>

示例 5:与原生filter_input()的比较

<?php
// 模拟GET数据
$_GET = [
    'id' => '123',
    'email' => 'test@example.com',
    'page' => 'invalid'
];

echo "<h4>原生filter_input():</h4>";

// 使用原生函数
$id1 = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
$email1 = filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL);
$page1 = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);

echo "ID: " . ($id1 !== false && $id1 !== null ? $id1 : '无效') . "<br>";
echo "Email: " . ($email1 !== false && $email1 !== null ? $email1 : '无效') . "<br>";
echo "Page: " . ($page1 !== false && $page1 !== null ? $page1 : '无效') . "<br>";

echo "<hr><h4>自定义filter_input_validate():</h4>";

// 使用自定义函数
$id2 = filter_input_validate(INPUT_GET, 'id', 'required|int:1,999');
$email2 = filter_input_validate(INPUT_GET, 'email', 'required|email');
$page2 = filter_input_validate(INPUT_GET, 'page', 'int:1,100', 1);

echo "ID: " . ($id2['valid'] ? $id2['value'] : '无效 - ' . $id2['error']) . "<br>";
echo "Email: " . ($email2['valid'] ? $email2['value'] : '无效 - ' . $email2['error']) . "<br>";
echo "Page: " . ($page2['valid'] ? $page2['value'] : '默认值: ' . $page2['value']) . "<br>";

echo "<hr><h4>优势对比:</h4>";
echo "<ul>";
echo "<li>原生函数返回简单值,自定义函数返回详细结果数组</li>";
echo "<li>自定义函数支持更丰富的验证规则</li>";
echo "<li>自定义函数提供更好的错误信息</li>";
echo "<li>自定义函数支持默认值设置</li>";
echo "<li>自定义函数支持批量验证</li>";
echo "</ul>";
?>

高级用法

扩展验证函数

<?php
/**
 * 扩展的验证类
 */
class AdvancedValidator {
    private static $customRules = [];

    /**
     * 添加自定义验证规则
     */
    public static function addRule($name, $callback) {
        self::$customRules[$name] = $callback;
    }

    /**
     * 增强的验证函数
     */
    public static function validate($type, $field, $rules, $default = null) {
        $result = filter_input_validate($type, $field, $rules, $default);

        // 处理自定义规则
        if (!$result['valid']) {
            foreach (self::$customRules as $ruleName => $callback) {
                if (strpos($rules, $ruleName) !== false) {
                    if (call_user_func($callback, $result['value'])) {
                        // 自定义规则通过,修正验证结果
                        $result['valid'] = true;
                        $result['error'] = null;
                        $result['errors'] = [];
                        break;
                    }
                }
            }
        }

        return $result;
    }

    /**
     * 验证整个表单
     */
    public static function validateForm($type, $rulesMap) {
        $results = [];
        $isValid = true;

        foreach ($rulesMap as $field => $rules) {
            $result = self::validate($type, $field, $rules);
            $results[$field] = $result;

            if (!$result['valid']) {
                $isValid = false;
            }
        }

        return [
            'valid' => $isValid,
            'results' => $results
        ];
    }
}

// 使用扩展验证类
AdvancedValidator::addRule('phone', 'validate_phone_number');
AdvancedValidator::addRule('strong_password', 'validate_strong_password');

$_POST = [
    'phone' => '+12345678901',
    'password' => 'StrongPass123'
];

$result = AdvancedValidator::validateForm(INPUT_POST, [
    'phone' => 'required|phone',
    'password' => 'required|strong_password|min:8'
]);

echo "<h4>扩展验证类结果:</h4>";
echo "<pre>" . print_r($result, true) . "</pre>";
?>

注意事项

  • 非标准函数:这不是PHP内置函数,需要自行实现
  • 性能考虑:复杂的验证规则可能会影响性能,特别是批量验证时
  • 安全性:自定义回调函数应进行安全检查,避免注入攻击
  • 错误处理:生产环境中应妥善处理验证失败的情况
  • 规则顺序:验证规则按顺序执行,建议先检查必填字段
  • 默认值:使用默认值时要考虑业务逻辑的合理性
  • 扩展性:可以根据需要添加更多验证规则
  • 测试:所有自定义验证规则都应进行充分测试

最佳实践

  1. 创建验证函数库:将常用验证规则封装为函数库
  2. 使用配置驱动:将验证规则存储在配置文件或数据库中
  3. 提供清晰的错误信息:帮助用户理解验证失败原因
  4. 支持国际化:错误信息应考虑多语言支持
  5. 记录验证日志:重要的验证失败可以记录日志
  6. 进行性能测试:确保验证逻辑不会成为性能瓶颈
  7. 保持向后兼容:更新验证规则时考虑现有数据
  8. 编写单元测试:确保验证逻辑的正确性

浏览器支持

该函数是 PHP 后端函数,与浏览器无关。需要 PHP 5.6 或更高版本。