PHP 过滤器

filter_var's validate and sanitise filters, and why it should be the default.

Beginner
Key Points`filter_var` is a built-in, audited implementation — more reliable than hand-rolled regex.

Validate vs sanitise

Two different goals: validate answers "is this legal", sanitise answers "what remains after stripping".

PHP
<?php
// 验证:合法返回值,非法返回 false
var_dump(filter_var("tom@example.com", FILTER_VALIDATE_EMAIL));
var_dump(filter_var("not-an-email", FILTER_VALIDATE_EMAIL));

var_dump(filter_var("42", FILTER_VALIDATE_INT));
var_dump(filter_var("4.2", FILTER_VALIDATE_INT));

var_dump(filter_var("http://example.com", FILTER_VALIDATE_URL));

// 清洗:总是返回字符串
echo filter_var("<b>hi</b>", FILTER_SANITIZE_SPECIAL_CHARS), "\n";
Output
string(15) "tom@example.com"
bool(false)
int(42)
bool(false)
string(18) "http://example.com"
&lt;b&gt;hi&lt;/b&gt;

Ranges and options

PHP
<?php
$opt = ["options" => ["min_range" => 1, "max_range" => 255]];

var_dump(filter_var("300", FILTER_VALIDATE_INT, $opt));
var_dump(filter_var("80", FILTER_VALIDATE_INT, $opt));

// 允许前导零的写法
var_dump(filter_var("007", FILTER_VALIDATE_INT));

// 允许八进制 / 十六进制
var_dump(filter_var("0x1A", FILTER_VALIDATE_INT, ["flags" => FILTER_FLAG_ALLOW_HEX]));

// 校验失败时返回默认值
var_dump(filter_var("abc", FILTER_VALIDATE_INT, ["options" => ["default" => 0]]));
Output
bool(false)
int(80)
bool(false)
int(26)
int(0)

"007" fails because leading zeros are not standard decimal. FILTER_FLAG_ALLOW_OCTAL accepts it but reads it as octal.

Common filters

FilterAccepts
FILTER_VALIDATE_EMAIL符合 RFC 的邮箱
FILTER_VALIDATE_URL带协议的 URL
FILTER_VALIDATE_IPIPv4 / IPv6
FILTER_VALIDATE_MACMAC 地址
FILTER_VALIDATE_DOMAIN域名
FILTER_VALIDATE_BOOLEAN1/0/true/false/on/off/yes/no
FILTER_VALIDATE_REGEXP自定义正则
FILTER_SANITIZE_EMAIL删掉邮箱里不允许的字符
FILTER_SANITIZE_NUMBER_INT只留数字和正负号
FILTER_SANITIZE_URL删掉 URL 里不允许的字符
FILTER_SANITIZE_SPECIAL_CHARSHTML 转义
PHP
<?php
var_dump(filter_var("192.168.1.1", FILTER_VALIDATE_IP));
var_dump(filter_var("::1", FILTER_VALIDATE_IP));
var_dump(filter_var("192.168.1.1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4));

var_dump(filter_var("yes", FILTER_VALIDATE_BOOLEAN));
var_dump(filter_var("no", FILTER_VALIDATE_BOOLEAN));
var_dump(filter_var("maybe", FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));

echo filter_var("abc123-45", FILTER_SANITIZE_NUMBER_INT), "\n";
echo filter_var("a b@c d.com", FILTER_SANITIZE_EMAIL), "\n";
Output
string(11) "192.168.1.1"
string(3) "::1"
string(11) "192.168.1.1"
bool(true)
bool(false)
NULL
123-45
ab@cd.com

With FILTER_NULL_ON_FAILURE, unrecognised values return null instead of false — distinguishing "explicitly false" from "not a boolean".

Custom regex filter

PHP
<?php
$filter = [
    "filter"  => FILTER_VALIDATE_REGEXP,
    "options" => ["regexp" => "/^[A-Za-z][A-Za-z0-9_]{5,17}$/"],
];

foreach (["Tom_2023", "1abc", "ab"] as $v) {
    $ok = filter_var($v, $filter["filter"], $filter["options"]) !== false;
    echo $v . "  " . ($ok ? "合法" : "非法"), "\n";
}
Output
Tom_2023  合法
1abc  非法
ab  非法
FILTER_VALIDATE_REGEXP uses preg_match semantics, so anchor with ^ and $ yourself — otherwise partial matches pass.

Why prefer filter

  • Audited —— edge cases like IPv6, IDN and timezones are handled
  • Stable —— a missing regex character does not silently pass bad input
  • Readable —— clearer than a long regex
  • But not strict enough —— business rules like phone prefixes or ID checksums are yours to write
FILTER_VALIDATE_EMAIL accepts a@b (RFC allows it). If a TLD is required, add your own check.

Friends & Partners