PHP 过滤与验证:filter_var、表单验证、数据清洗

小飞兽 PHP 14 次阅读 2026-07-16

为什么需要过滤验证

永远不要相信用户输入。所有来自外部的数据($_GET、$_POST、$_COOKIE、数据库、外部 API)都需要过滤和验证。

一、filter_var 基础

filter_var 是 PHP 内置的过滤函数,支持多种过滤类型。

<?php\n// 验证邮箱\necho filter_var('test@example.com', FILTER_VALIDATE_EMAIL);    // 'test@example.com'(通过)\necho filter_var('invalid-email', FILTER_VALIDATE_EMAIL);        // false(失败)\n\n// 验证 URL\necho filter_var('https://example.com', FILTER_VALIDATE_URL);    // 'https://example.com'\necho filter_var('not-a-url', FILTER_VALIDATE_URL);              // false\n\n// 验证 IP\necho filter_var('192.168.1.1', FILTER_VALIDATE_IP);             // '192.168.1.1'\necho filter_var('256.1.1.1', FILTER_VALIDATE_IP);               // false\n\n// 验证整数\nfilter_var(42, FILTER_VALIDATE_INT);                            // 42\necho filter_var(3.14, FILTER_VALIDATE_INT);                     // false\n\n// 验证浮点数\nfilter_var(3.14, FILTER_VALIDATE_FLOAT);                        // 3.14\n?>

二、filter_var 过滤选项

<?php\n// 整数范围过滤\n$options = ['options' => ['min_range' => 1, 'max_range' => 120]];\nfilter_var(150, FILTER_VALIDATE_INT, $options);  // false(超出范围)\nfilter_var(25, FILTER_VALIDATE_INT, $options);   // 25\n\n// 浮点数范围\n$options = ['options' => ['min_range' => 0, 'max_range' => 100]];\nfilter_var(99.5, FILTER_VALIDATE_FLOAT, $options);  // 99.5\n\n// URL 协议过滤\n$options = ['options' => ['default' => 'http', 'protocols' => ['http', 'https']]];\necho filter_var('ftp://example.com', FILTER_VALIDATE_URL, $options); // false\n?>

三、sanitize 过滤器(清洗数据)

<?php\n// 清洗邮箱(只保留合法邮箱字符)\n$email = '  test@example.com  ';\necho filter_var($email, FILTER_SANITIZE_EMAIL);\n// test@example.com(去除了空格和非法字符)\n\n// 清洗 URL(只保留合法 URL 字符)\n$url = 'https://example.com/?url=';\necho filter_var($url, FILTER_SANITIZE_URL);\n// https://example.com/?url=scriptalert(1)/script\n\n// 清洗字符串(去除/编码特殊字符)\n$str = '';\necho filter_var($str, FILTER_SANITIZE_STRING);  // <script>alert("xss")</script>\n\n// 清洗 HTML 特殊字符\necho htmlspecialchars($str, ENT_QUOTES, 'UTF-8');\n// &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;\n\n// 清洗数字(只保留数字和 +-)\necho filter_var('+86-138-0013-8000', FILTER_SANITIZE_NUMBER_INT);\n// 86-138-0013-8000\n\n// 清洗浮点数\necho filter_var('99.9元', FILTER_SANITIZE_NUMBER_FLOAT);\n// 99.9\n?>

四、filter_input — 直接过滤超全局变量

<?php\n// filter_input 从超全局变量获取并过滤,一步完成\n\n// 过滤 POST 提交的邮箱\n$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);\nif ($email === false) {\n    echo '无效的邮箱';\n}\n\n// 过滤 GET 提交的整数\n$page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, ['options' => ['default' => 1, 'min_range' => 1]]);\n\n// 过滤并清洗\n$comment = filter_input(INPUT_POST, 'comment', FILTER_SANITIZE_FULL_SPECIAL_CHARS);\n\n// 过滤多个变量\n$filters = [\n    'username' => ['filter' => FILTER_SANITIZE_STRING],\n    'email'    => FILTER_VALIDATE_EMAIL,\n    'age'      => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 150]],\n];\n$clean = filter_input_array(INPUT_POST, $filters);\nprint_r($clean);\n?>

五、完整的表单验证类

<?php\nclass Validator {\n    private array $errors = [];\n    private array $data = [];\n\n    public function __construct(private array $rules) {}\n\n    public function validate(array $input): bool {\n        foreach ($this->rules as $field => $rule) {\n            $value = $input[$field] ?? null;\n            $result = $this->applyRule($field, $value, $rule);\n            if ($result !== false) {\n                $this->data[$field] = $result;\n            }\n        }\n        return empty($this->errors);\n    }\n\n    private function applyRule(string $field, mixed $value, mixed $rule): mixed {\n        // 字符串规则:'required|email|min:3'\n        if (is_string($rule)) {\n            $rules = explode('|', $rule);\n            foreach ($rules as $r) {\n                [$name, $param] = array_pad(explode(':', $r, 2), 2, null);\n                $result = $this->validateRule($field, $value, $name, $param);\n                if ($result === false) return false;\n            }\n            return $value;\n        }\n        return $this->validateRule($field, $value, 'custom', $rule);\n    }\n\n    private function validateRule(string $f, mixed $v, string $r, mixed $p): mixed {\n        switch ($r) {\n            case 'required':\n                if ($v === null || $v === '' || $v === []) {\n                    $this->errors[$f][] = \"{$f}不能为空\"; return false;\n                }\n                break;\n            case 'email':\n                if ($v && !filter_var($v, FILTER_VALIDATE_EMAIL)) {\n                    $this->errors[$f][] = \"{$f}邮箱格式不正确\"; return false;\n                }\n                break;\n            case 'min':\n                if (mb_strlen($v) < (int)$p) {\n                    $this->errors[$f][] = \"{$f}至少{$p}个字符\"; return false;\n                }\n                break;\n            case 'max':\n                if (mb_strlen($v) > (int)$p) {\n                    $this->errors[$f][] = \"{$f}最多{$p}个字符\"; return false;\n                }\n                break;\n            case 'int':\n                if ($v && filter_var($v, FILTER_VALIDATE_INT) === false) {\n                    $this->errors[$f][] = \"{$f}必须是整数\"; return false;\n                }\n                $v = (int)$v; break;\n            case 'url':\n                if ($v && !filter_var($v, FILTER_VALIDATE_URL)) {\n                    $this->errors[$f][] = \"{$f}必须是有效URL\"; return false;\n                }\n                break;\n            case 'in':\n                $allowed = is_array($p) ? $p : explode(',', $p);\n                if (!in_array($v, $allowed)) {\n                    $this->errors[$f][] = \"{$f}值不在允许范围内\"; return false;\n                }\n                break;\n        }\n        return $v;\n    }\n\n    public function getErrors(): array { return $this->errors; }\n    public function getData(): array { return $this->data; }\n}\n\n// 使用\n$v = new Validator([\n    'username' => 'required|min:3|max:20',\n    'email'    => 'required|email',\n    'age'      => 'int|min:1|max:150',\n    'role'     => 'required|in:admin,editor,user',\n]);\n\nif ($v->validate($_POST)) {\n    $data = $v->getData();\n    // 入库...\n} else {\n    print_r($v->getErrors());\n}\n?>

六、XSS 防护

<?php\n// 输出到 HTML:转义\necho htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');\n\n// HTML Purifier(允许部分 HTML 标签)\nrequire 'HTMLPurifier.auto.php';\n$config = HTMLPurifier_Config::createDefault();\n$config->set('HTML.Allowed', 'p,br,b,i,a[href],ul,li');\n$purifier = new HTMLPurifier($config);\n$clean = $purifier->purify($user_input);\n\n// 纯文本(strip all tags)\necho strip_tags($user_input);\n?>

总结

    • filter_var(value, FILTER_VALIDATE_*) 用于验证,返回 false 表示失败
    • filter_var(value, FILTER_SANITIZE_*) 用于清洗,返回清洗后的值
    • filter_input() 直接从超全局变量获取并过滤,更安全
  • 永远先验证再清洗,顺序不能反
  • htmlspecialchars + ENT_QUOTES + UTF-8 是防 XSS 的标准做法