PHP 过滤与验证:filter_var、表单验证、数据清洗
为什么需要过滤验证
永远不要相信用户输入。所有来自外部的数据($_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// <script>alert("xss")</script>\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 的标准做法