PHP filter_var() 函数

定义和用法

filter_var() 函数用于使用指定的过滤器过滤单个变量。这是PHP过滤器扩展中最常用的函数之一。

该函数可以用于验证和清理各种类型的数据,如邮箱、URL、整数、浮点数、字符串等。

提示:使用 filter_var() 函数可以提高应用的安全性,防止常见的安全漏洞如XSS攻击和SQL注入。

语法

filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed

参数值

参数 描述
$value 要过滤的值。可以是任何类型的变量。
$filter

过滤器类型(可选)。默认为 FILTER_DEFAULT(不进行过滤)。

可以是以下类型之一:

  • 验证过滤器:验证数据格式,失败返回 false
  • 清理过滤器:清理数据中的不安全字符
  • 其他过滤器:如 FILTER_CALLBACK 使用自定义函数
$options

过滤器选项(可选)。可以是以下形式之一:

  1. 标志(flags):一个或多个标志位,用按位或(|)连接
  2. 选项数组:包含配置选项的关联数组

对于某些过滤器,可以指定验证范围、默认值等。

常用过滤器类型

过滤器类型 常量 描述 返回值
验证整数 FILTER_VALIDATE_INT 验证是否为整数,可指定范围 整数或 false
验证布尔值 FILTER_VALIDATE_BOOLEAN 验证是否为布尔值 布尔值或 false
验证浮点数 FILTER_VALIDATE_FLOAT 验证是否为浮点数 浮点数或 false
验证邮箱 FILTER_VALIDATE_EMAIL 验证是否为有效的邮箱地址 邮箱或 false
验证URL FILTER_VALIDATE_URL 验证是否为有效的URL URL或 false
验证IP FILTER_VALIDATE_IP 验证是否为有效的IP地址 IP或 false
清理字符串 FILTER_SANITIZE_STRING 去除或编码HTML标签和特殊字符 清理后的字符串
清理邮箱 FILTER_SANITIZE_EMAIL 去除邮箱地址中非法字符 清理后的邮箱
清理URL FILTER_SANITIZE_URL 去除URL中非法字符 清理后的URL
自定义回调 FILTER_CALLBACK 使用自定义函数过滤 回调函数返回值

常用过滤器标志

标志 描述 适用过滤器
FILTER_FLAG_ALLOW_FRACTION 允许浮点数有小数部分 FILTER_VALIDATE_FLOAT
FILTER_FLAG_ALLOW_THOUSAND 允许千位分隔符(逗号) FILTER_VALIDATE_FLOAT
FILTER_FLAG_ALLOW_SCIENTIFIC 允许科学计数法 FILTER_VALIDATE_FLOAT
FILTER_FLAG_IPV4 仅允许IPv4地址 FILTER_VALIDATE_IP
FILTER_FLAG_IPV6 仅允许IPv6地址 FILTER_VALIDATE_IP
FILTER_FLAG_NO_PRIV_RANGE 不允许私有IP范围 FILTER_VALIDATE_IP
FILTER_FLAG_NO_RES_RANGE 不允许保留IP范围 FILTER_VALIDATE_IP
FILTER_FLAG_PATH_REQUIRED URL必须包含路径 FILTER_VALIDATE_URL
FILTER_FLAG_QUERY_REQUIRED URL必须包含查询字符串 FILTER_VALIDATE_URL
FILTER_FLAG_STRIP_LOW 去除ASCII值低于32的字符 FILTER_SANITIZE_STRING
FILTER_FLAG_STRIP_HIGH 去除ASCII值高于127的字符 FILTER_SANITIZE_STRING
FILTER_FLAG_ENCODE_LOW 编码ASCII值低于32的字符 FILTER_SANITIZE_STRING
FILTER_FLAG_ENCODE_HIGH 编码ASCII值高于127的字符 FILTER_SANITIZE_STRING

返回值

  • 验证过滤器:验证成功返回过滤后的值,失败返回 false
  • 清理过滤器:始终返回清理后的值
  • 自定义回调:返回回调函数的返回值
  • 默认过滤器:返回原始值,不进行过滤

示例

示例 1:基本验证

<?php
// 验证整数
$int = "123";
if (filter_var($int, FILTER_VALIDATE_INT)) {
    echo "$int 是有效的整数<br>";
} else {
    echo "$int 不是有效的整数<br>";
}

// 验证邮箱
$email = "user@example.com";
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
    echo "$email 是有效的邮箱地址<br>";
} else {
    echo "$email 不是有效的邮箱地址<br>";
}

// 验证URL
$url = "https://www.example.com";
if (filter_var($url, FILTER_VALIDATE_URL)) {
    echo "$url 是有效的URL<br>";
} else {
    echo "$url 不是有效的URL<br>";
}
?>
输出:
123 是有效的整数
user@example.com 是有效的邮箱地址
https://www.example.com 是有效的URL

示例 2:使用选项和标志

<?php
// 验证整数范围
$age = "25";
$options = array(
    'options' => array(
        'min_range' => 18,
        'max_range' => 60,
        'default' => 0
    )
);
$filtered_age = filter_var($age, FILTER_VALIDATE_INT, $options);
if ($filtered_age !== false) {
    echo "年龄: $filtered_age<br>";
} else {
    echo "年龄无效或不在范围内<br>";
}

// 验证浮点数(允许小数和千位分隔符)
$price = "1,250.50";
$filtered_price = filter_var($price, FILTER_VALIDATE_FLOAT,
    FILTER_FLAG_ALLOW_THOUSAND | FILTER_FLAG_ALLOW_FRACTION);
if ($filtered_price !== false) {
    echo "价格: $filtered_price<br>";
} else {
    echo "价格无效<br>";
}

// 验证IP地址(仅IPv4)
$ip = "192.168.1.1";
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
    echo "$ip 是有效的IPv4地址<br>";
} else {
    echo "$ip 不是有效的IPv4地址<br>";
}
?>

示例 3:数据清理

<?php
// 清理字符串(去除HTML标签)
$string = "Hello <b>World</b>! <script>alert('xss')</script>";
$cleaned = filter_var($string, FILTER_SANITIZE_STRING);
echo "原始字符串: " . htmlspecialchars($string) . "<br>";
echo "清理后: " . htmlspecialchars($cleaned) . "<br><br>";

// 清理字符串(去除ASCII值低于32和高于127的字符)
$string2 = "Hello\x00World!\x01";
$cleaned2 = filter_var($string2, FILTER_SANITIZE_STRING,
    FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
echo "原始字符串(包含控制字符): " . htmlspecialchars(bin2hex($string2)) . "<br>";
echo "清理后: " . htmlspecialchars($cleaned2) . "<br><br>";

// 清理邮箱地址
$email = "test()@example.com";
$cleaned_email = filter_var($email, FILTER_SANITIZE_EMAIL);
echo "原始邮箱: $email<br>";
echo "清理后邮箱: $cleaned_email<br><br>";

// 清理URL
$url = "https://example.com/path?query=<script>alert(1)</script>";
$cleaned_url = filter_var($url, FILTER_SANITIZE_URL);
echo "原始URL: " . htmlspecialchars($url) . "<br>";
echo "清理后URL: " . htmlspecialchars($cleaned_url) . "<br>";
?>

示例 4:自定义回调过滤器

<?php
// 使用自定义函数验证用户名
function validate_username($value) {
    // 用户名规则:3-20个字符,只能包含字母、数字和下划线
    if (preg_match('/^[a-zA-Z0-9_]{3,20}$/', $value)) {
        return $value;
    }
    return false;
}

$username = "john_doe123";
$filtered_username = filter_var($username, FILTER_CALLBACK,
    array('options' => 'validate_username'));
if ($filtered_username !== false) {
    echo "用户名有效: $filtered_username<br>";
} else {
    echo "用户名无效<br>";
}

// 使用匿名函数转换数据格式
$date_string = "2023-12-31";
$formatted_date = filter_var($date_string, FILTER_CALLBACK,
    array('options' => function($value) {
        $date = DateTime::createFromFormat('Y-m-d', $value);
        return $date ? $date->format('d/m/Y') : false;
    }));
echo "原始日期: $date_string<br>";
echo "格式化后日期: $formatted_date<br><br>";

// 批量处理数组
function sanitize_array($array) {
    $result = [];
    foreach ($array as $key => $value) {
        // 对每个值应用清理
        $result[$key] = filter_var($value, FILTER_SANITIZE_STRING);
    }
    return $result;
}

$data = ['name' => 'John<script>', 'age' => '25<b>'];
$sanitized = filter_var($data, FILTER_CALLBACK,
    array('options' => 'sanitize_array'));
echo "原始数组: " . print_r($data, true) . "<br>";
echo "清理后数组: " . print_r($sanitized, true) . "<br>";
?>

示例 5:表单验证完整示例

<?php
// 表单验证类
class FormValidator {
    private $errors = [];

    public function validate($field, $value, $rules) {
        foreach ($rules as $rule => $params) {
            switch ($rule) {
                case 'required':
                    if (empty(trim($value))) {
                        $this->errors[$field] = "$field 是必填字段";
                        return false;
                    }
                    break;

                case 'email':
                    if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                        $this->errors[$field] = "$field 必须是有效的邮箱地址";
                        return false;
                    }
                    break;

                case 'int':
                    $options = isset($params['min']) && isset($params['max']) ?
                        array('options' => array('min_range' => $params['min'], 'max_range' => $params['max'])) :
                        array();

                    if (filter_var($value, FILTER_VALIDATE_INT, $options) === false) {
                        $range_text = isset($params['min']) ? "({$params['min']}-{$params['max']})" : '';
                        $this->errors[$field] = "$field 必须是有效的整数$range_text";
                        return false;
                    }
                    break;

                case 'float':
                    $flags = 0;
                    if (isset($params['allow_thousand']) && $params['allow_thousand']) {
                        $flags |= FILTER_FLAG_ALLOW_THOUSAND;
                    }
                    if (isset($params['allow_fraction']) && $params['allow_fraction']) {
                        $flags |= FILTER_FLAG_ALLOW_FRACTION;
                    }

                    if (filter_var($value, FILTER_VALIDATE_FLOAT, $flags) === false) {
                        $this->errors[$field] = "$field 必须是有效的浮点数";
                        return false;
                    }
                    break;

                case 'url':
                    $flags = 0;
                    if (isset($params['require_path']) && $params['require_path']) {
                        $flags |= FILTER_FLAG_PATH_REQUIRED;
                    }
                    if (isset($params['require_query']) && $params['require_query']) {
                        $flags |= FILTER_FLAG_QUERY_REQUIRED;
                    }

                    if (!filter_var($value, FILTER_VALIDATE_URL, $flags)) {
                        $this->errors[$field] = "$field 必须是有效的URL";
                        return false;
                    }
                    break;

                case 'sanitize_string':
                    $flags = 0;
                    if (isset($params['strip_low']) && $params['strip_low']) {
                        $flags |= FILTER_FLAG_STRIP_LOW;
                    }
                    if (isset($params['strip_high']) && $params['strip_high']) {
                        $flags |= FILTER_FLAG_STRIP_HIGH;
                    }

                    return filter_var($value, FILTER_SANITIZE_STRING, $flags);

                case 'custom':
                    if (isset($params['callback']) && is_callable($params['callback'])) {
                        $result = call_user_func($params['callback'], $value);
                        if ($result === false) {
                            $this->errors[$field] = isset($params['message']) ?
                                $params['message'] : "$field 验证失败";
                            return false;
                        }
                        return $result;
                    }
                    break;
            }
        }

        return $value;
    }

    public function getErrors() {
        return $this->errors;
    }

    public function hasErrors() {
        return !empty($this->errors);
    }
}

// 使用示例
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $validator = new FormValidator();

    // 验证规则
    $rules = [
        'name' => [
            'required' => true,
            'sanitize_string' => ['strip_low' => true, 'strip_high' => true]
        ],
        'email' => [
            'required' => true,
            'email' => true
        ],
        'age' => [
            'required' => true,
            'int' => ['min' => 18, 'max' => 100]
        ],
        'salary' => [
            'float' => ['allow_thousand' => true, 'allow_fraction' => true]
        ],
        'website' => [
            'url' => ['require_path' => true]
        ],
        'username' => [
            'custom' => [
                'callback' => function($value) {
                    return preg_match('/^[a-zA-Z0-9_]{3,20}$/', $value);
                },
                'message' => '用户名必须是3-20个字符,只能包含字母、数字和下划线'
            ]
        ]
    ];

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

    $validated_data = [];
    foreach ($rules as $field => $field_rules) {
        $value = isset($_POST[$field]) ? $_POST[$field] : '';
        $validated_data[$field] = $validator->validate($field, $value, $field_rules);
    }

    if ($validator->hasErrors()) {
        echo "<div class='alert alert-danger'>";
        echo "<h4>验证错误:</h4>";
        foreach ($validator->getErrors() as $field => $error) {
            echo "<p>$error</p>";
        }
        echo "</div>";
    } else {
        echo "<div class='alert alert-success'>";
        echo "<h4>所有数据验证通过!</h4>";
        echo "<pre>" . print_r($validated_data, true) . "</pre>";
        echo "</div>";
    }
}
?>

注意事项

  • 验证失败返回值:验证过滤器失败时返回 false,注意与 0、空字符串等区分
  • 类型转换:验证过滤器会进行类型转换,如 "123" 会被转换为整数 123
  • 默认过滤器FILTER_DEFAULT 相当于 FILTER_UNSAFE_RAW,不进行任何过滤
  • 组合使用:可以先使用清理过滤器,再使用验证过滤器
  • 性能考虑:对于大量数据,考虑使用 filter_var_array()
  • 错误处理:始终检查返回值是否为 false
  • HTML输出:即使使用了过滤器,输出到HTML时仍应使用 htmlspecialchars()
  • 安全性:过滤器不能替代参数化查询,防止SQL注入
  • 自定义验证:复杂的验证逻辑应使用自定义回调或专门的验证库

与相关函数的比较

方面 filter_var() filter_input() filter_var_array()
数据来源 直接传递的变量 输入源(GET/POST/COOKIE等) 数组中的变量
处理数量 单个变量 单个变量 多个变量
使用场景 验证/清理单个值 从用户输入获取单个值 批量处理数组数据
性能 适合简单场景 适合单个输入 批量处理效率高
配置复杂性 简单 简单 复杂(需要配置数组)
安全性 高(需要正确使用) 高(直接处理输入) 高(批量处理)

最佳实践

  1. 始终验证用户输入:所有外部数据都应经过验证和清理
  2. 使用适当的过滤器:根据数据类型选择合适的过滤器
  3. 检查返回值:验证过滤器失败返回 false,要正确检查
  4. 组合验证和清理:先清理再验证,或先验证再清理
  5. 使用选项限制范围:对于数值,使用选项限制最小和最大值
  6. 自定义验证:复杂规则使用 FILTER_CALLBACK 或自定义验证类
  7. 错误信息:提供清晰的错误信息,帮助用户修正输入
  8. 输出编码:即使数据已经过滤,输出时仍要进行HTML编码
  9. 性能优化:对于大量数据,使用批量处理函数
  10. 测试边界条件:测试空值、特殊字符、边界值等情况

浏览器支持

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