PHP filter_var_array() 函数

定义和用法

filter_var_array() 函数用于批量过滤多个变量。这个函数可以一次性对多个值应用不同的过滤器,非常适合处理表单数据或配置数组。

filter_input_array() 不同,filter_var_array() 直接从变量数组获取数据,而不是从输入源(如GET、POST)获取。

提示:这个函数是处理批量数据验证和清理的理想工具,可以大大简化代码并提高安全性。

语法

filter_var_array(array $data, array|int $definition = FILTER_DEFAULT, bool $add_empty = true): array|false|null

参数值

参数 描述
$data 要过滤的数据数组。一个包含要过滤的键值对的数组。
$definition

过滤器定义。可以是以下两种形式之一:

  1. 过滤器ID(整数):所有数据使用相同的过滤器
  2. 配置数组:为每个键指定不同的过滤器配置

配置数组的格式如下:

$definition = [
    'field1' => FILTER_SANITIZE_STRING,
    'field2' => [
        'filter'  => FILTER_VALIDATE_INT,
        'options' => ['min_range' => 1, 'max_range' => 100],
        'flags'   => FILTER_REQUIRE_SCALAR
    ]
];
$add_empty 是否包含空值(可选)。默认为 true
  • true:在结果中包含缺失的键,值为 null
  • false:在结果中排除缺失的键

返回值

  • 成功时:返回包含过滤后值的数组
  • 键不存在时:根据 $add_empty 参数决定是否包含 null
  • 发生错误时:返回 false
  • 输入数据为空时:返回 null

示例

示例 1:基本用法 - 批量过滤数组

<?php
// 要过滤的数据
$data = [
    'name'  => 'John <script>alert("xss")</script> Doe',
    'age'   => '25',
    'email' => 'john@example.com',
    'score' => '95.5'
];

// 过滤器定义
$filters = [
    'name'  => FILTER_SANITIZE_STRING,
    'age'   => FILTER_VALIDATE_INT,
    'email' => FILTER_VALIDATE_EMAIL,
    'score' => FILTER_VALIDATE_FLOAT
];

// 批量过滤
$result = filter_var_array($data, $filters);

echo "<h4>原始数据:</h4>";
echo "<pre>" . htmlspecialchars(print_r($data, true)) . "</pre>";

echo "<h4>过滤后的数据:</h4>";
echo "<pre>" . print_r($result, true) . "</pre>";

// 检查过滤结果
foreach ($result as $key => $value) {
    echo "<p>";
    if ($value === false) {
        echo "<span class='text-danger'>$key: 验证失败</span>";
    } elseif ($value === null) {
        echo "<span class='text-warning'>$key: 值为空或不存在</span>";
    } else {
        echo "<span class='text-success'>$key: " . htmlspecialchars($value) . "</span>";
    }
    echo "</p>";
}
?>

示例 2:使用高级配置选项

<?php
// 复杂的过滤配置
$data = [
    'username'  => 'user_123<script>alert(1)</script>',
    'password'  => 'myp@ssw0rd',
    'age'       => '30',
    'email'     => 'test@example.com',
    'website'   => 'https://www.example.com/path?query=1',
    'ip_address'=> '192.168.1.1',
    'categories'=> [1, 2, 3, 'invalid'], // 数组数据
    'tags'      => ['php', 'javascript', 'html']
];

$filters = [
    'username' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'flags'   => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH,
        'options' => ['default' => 'anonymous']
    ],
    'password' => FILTER_UNSAFE_RAW, // 不过滤密码,稍后哈希处理
    'age' => [
        'filter'  => FILTER_VALIDATE_INT,
        'options' => [
            'min_range' => 18,
            'max_range' => 100,
            'default'   => 0
        ]
    ],
    'email' => FILTER_VALIDATE_EMAIL,
    'website' => [
        'filter' => FILTER_VALIDATE_URL,
        'flags'  => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED
    ],
    'ip_address' => [
        'filter' => FILTER_VALIDATE_IP,
        'flags'  => FILTER_FLAG_IPV4
    ],
    'categories' => [
        'filter' => FILTER_VALIDATE_INT,
        'flags'  => FILTER_REQUIRE_ARRAY | FILTER_FLAG_ARRAY_FILTER
    ],
    'tags' => [
        'filter' => FILTER_SANITIZE_STRING,
        'flags'  => FILTER_REQUIRE_ARRAY
    ]
];

$result = filter_var_array($data, $filters);

echo "<h4>过滤结果:</h4>";
echo "<div style='max-height: 400px; overflow-y: auto;'>";
echo "<pre>" . print_r($result, true) . "</pre>";
echo "</div>";

// 统计验证结果
$validCount = 0;
$invalidCount = 0;

foreach ($result as $key => $value) {
    if ($value === false) {
        $invalidCount++;
        echo "<p class='text-danger'>$key: 验证失败</p>";
    } elseif (is_array($value) && in_array(false, $value, true)) {
        $invalidCount++;
        echo "<p class='text-danger'>$key: 数组中包含无效值</p>";
    } else {
        $validCount++;
    }
}

echo "<div class='alert alert-info mt-3'>";
echo "验证统计:$validCount 个字段通过验证,$invalidCount 个字段验证失败";
echo "</div>";
?>

示例 3:处理表单提交数据

<?php
// 模拟表单提交数据
$formData = [
    'first_name'    => 'John',
    'last_name'     => 'Doe<script>alert("xss")</script>',
    'email'         => 'john.doe@example.com',
    'phone'         => '+1-555-123-4567',
    'age'           => '30',
    'salary'        => '55000.50',
    'website'       => 'https://example.com',
    'newsletter'    => 'on', // 复选框选中
    'interests'     => ['coding', 'reading', 'sports'], // 多选
    'bio'           => "Hello!\nI'm a developer with <b>5 years</b> experience."
];

// 定义过滤器配置
$filterConfig = [
    'first_name' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'flags'   => FILTER_FLAG_STRIP_LOW
    ],
    'last_name' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'flags'   => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH
    ],
    'email' => FILTER_VALIDATE_EMAIL,
    'phone' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'options' => ['default' => '']
    ],
    'age' => [
        'filter'  => FILTER_VALIDATE_INT,
        'options' => ['min_range' => 18, 'max_range' => 120]
    ],
    'salary' => [
        'filter'  => FILTER_VALIDATE_FLOAT,
        'options' => ['decimal' => 2],
        'flags'   => FILTER_FLAG_ALLOW_FRACTION
    ],
    'website' => FILTER_VALIDATE_URL,
    'newsletter' => FILTER_VALIDATE_BOOLEAN,
    'interests' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'flags'   => FILTER_REQUIRE_ARRAY
    ],
    'bio' => [
        'filter'  => FILTER_SANITIZE_SPECIAL_CHARS,
        'flags'   => FILTER_FLAG_ENCODE_AMP
    ]
];

// 应用过滤
$filteredData = filter_var_array($formData, $filterConfig);

// 显示结果
echo "<div class='row'>";
echo "<div class='col-md-6'>";
echo "<h4>原始表单数据:</h4>";
echo "<pre style='max-height: 300px; overflow-y: auto;'>" .
     htmlspecialchars(print_r($formData, true)) . "</pre>";
echo "</div>";

echo "<div class='col-md-6'>";
echo "<h4>过滤后的数据:</h4>";
echo "<pre style='max-height: 300px; overflow-y: auto;'>" .
     print_r($filteredData, true) . "</pre>";
echo "</div>";
echo "</div>";

// 验证结果处理
$errors = [];
$cleanData = [];

foreach ($filteredData as $field => $value) {
    if ($value === false) {
        $errors[$field] = "$field 验证失败";
    } elseif ($value === null) {
        // 忽略空值
    } else {
        $cleanData[$field] = $value;
    }
}

if (!empty($errors)) {
    echo "<div class='alert alert-danger mt-3'>";
    echo "<h5>验证错误:</h5>";
    foreach ($errors as $field => $error) {
        echo "<p>$error</p>";
    }
    echo "</div>";
} else {
    echo "<div class='alert alert-success mt-3'>";
    echo "<h5>数据验证通过!</h5>";
    echo "已成功清理 " . count($cleanData) . " 个字段的数据";
    echo "</div>";
}
?>

示例 4:批量处理数据库查询结果

<?php
// 模拟数据库查询结果
$databaseResults = [
    [
        'id' => '1',
        'name' => 'Product 1',
        'price' => '19.99',
        'quantity' => '100',
        'category' => 'electronics',
        'description' => 'A great product! <script>alert("xss")</script>'
    ],
    [
        'id' => '2',
        'name' => 'Product 2',
        'price' => '29.50',
        'quantity' => '50',
        'category' => 'clothing',
        'description' => 'Another product with <em>HTML</em> tags.'
    ],
    [
        'id' => '3',
        'name' => 'Product 3',
        'price' => 'invalid_price',
        'quantity' => '-10', // 无效数量
        'category' => 'books',
        'description' => 'A third product.'
    ]
];

// 定义过滤器配置
$filterDefinition = [
    'id' => FILTER_VALIDATE_INT,
    'name' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'flags'   => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_ENCODE_HIGH
    ],
    'price' => [
        'filter'  => FILTER_VALIDATE_FLOAT,
        'options' => ['min_range' => 0],
        'flags'   => FILTER_FLAG_ALLOW_FRACTION
    ],
    'quantity' => [
        'filter'  => FILTER_VALIDATE_INT,
        'options' => ['min_range' => 0]
    ],
    'category' => [
        'filter'  => FILTER_SANITIZE_STRING,
        'options' => ['default' => 'uncategorized']
    ],
    'description' => [
        'filter'  => FILTER_SANITIZE_SPECIAL_CHARS,
        'flags'   => FILTER_FLAG_ENCODE_AMP | FILTER_FLAG_ENCODE_QUOTES
    ]
];

// 批量处理所有记录
$processedResults = [];

foreach ($databaseResults as $index => $record) {
    $processed = filter_var_array($record, $filterDefinition);

    // 检查处理结果
    $hasErrors = false;
    foreach ($processed as $key => $value) {
        if ($value === false) {
            $hasErrors = true;
            $processed[$key] = "INVALID: " . $record[$key];
        }
    }

    if ($hasErrors) {
        $processed['_status'] = 'error';
    } else {
        $processed['_status'] = 'valid';
    }

    $processedResults[] = $processed;
}

// 显示处理结果
echo "<h4>数据库记录批量处理结果:</h4>";
echo "<table class='table table-bordered'>";
echo "<thead><tr><th>ID</th><th>名称</th><th>价格</th><th>数量</th><th>分类</th><th>状态</th></tr></thead>";
echo "<tbody>";

foreach ($processedResults as $record) {
    $statusClass = $record['_status'] === 'valid' ? 'success' : 'danger';

    echo "<tr class='table-$statusClass'>";
    echo "<td>" . htmlspecialchars($record['id']) . "</td>";
    echo "<td>" . htmlspecialchars($record['name']) . "</td>";
    echo "<td>" . htmlspecialchars($record['price']) . "</td>";
    echo "<td>" . htmlspecialchars($record['quantity']) . "</td>";
    echo "<td>" . htmlspecialchars($record['category']) . "</td>";
    echo "<td><span class='badge bg-$statusClass'>" . $record['_status'] . "</span></td>";
    echo "</tr>";
}

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

// 显示处理后的完整数据
echo "<details class='mt-3'>";
echo "<summary>查看完整处理数据</summary>";
echo "<pre style='max-height: 300px; overflow-y: auto;'>" .
     print_r($processedResults, true) . "</pre>";
echo "</details>";
?>

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

<?php
// 使用自定义回调函数作为过滤器
$userData = [
    'username'  => 'user123',
    'password'  => 'P@ssw0rd123',
    'email'     => 'test@example.com',
    'birthdate' => '1990-05-15',
    'custom_field' => 'custom_value'
];

// 定义自定义过滤器函数
function customUsernameFilter($value) {
    // 只允许字母、数字和下划线,长度3-20
    if (preg_match('/^[a-zA-Z0-9_]{3,20}$/', $value)) {
        return $value;
    }
    return false;
}

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

function customDateFilter($value) {
    // 验证日期格式并确保不是未来日期
    $date = DateTime::createFromFormat('Y-m-d', $value);
    if ($date && $date->format('Y-m-d') === $value) {
        $now = new DateTime();
        if ($date <= $now) {
            return $value;
        }
    }
    return false;
}

// 过滤器配置,包含自定义回调
$filters = [
    'username' => [
        'filter' => FILTER_CALLBACK,
        'options' => 'customUsernameFilter'
    ],
    'password' => [
        'filter' => FILTER_CALLBACK,
        'options' => 'customPasswordFilter'
    ],
    'email' => FILTER_VALIDATE_EMAIL,
    'birthdate' => [
        'filter' => FILTER_CALLBACK,
        'options' => 'customDateFilter'
    ],
    'custom_field' => [
        'filter' => FILTER_CALLBACK,
        'options' => function($value) {
            // 匿名函数作为过滤器
            return strtoupper($value);
        }
    ]
];

// 应用过滤
$result = filter_var_array($userData, $filters);

// 显示结果
echo "<h4>自定义过滤器结果:</h4>";
echo "<div class='table-responsive'>";
echo "<table class='table table-bordered'>";
echo "<thead><tr><th>字段</th><th>原始值</th><th>过滤结果</th><th>状态</th></tr></thead>";
echo "<tbody>";

foreach ($result as $field => $value) {
    $status = '';
    $statusClass = '';

    if ($value === false) {
        $status = '验证失败';
        $statusClass = 'danger';
    } elseif ($value === null) {
        $status = '空值';
        $statusClass = 'warning';
    } else {
        $status = '通过';
        $statusClass = 'success';
    }

    echo "<tr>";
    echo "<td><strong>$field</strong></td>";
    echo "<td>" . htmlspecialchars($userData[$field] ?? 'N/A') . "</td>";
    echo "<td>" . htmlspecialchars($value === false ? 'false' : (string)$value) . "</td>";
    echo "<td><span class='badge bg-$statusClass'>$status</span></td>";
    echo "</tr>";
}

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

// 统计信息
$validCount = 0;
$invalidCount = 0;

foreach ($result as $value) {
    if ($value === false) {
        $invalidCount++;
    } elseif ($value !== null) {
        $validCount++;
    }
}

echo "<div class='alert alert-secondary mt-3'>";
echo "自定义过滤器统计:$validCount 个字段通过验证,$invalidCount 个字段验证失败";
echo "</div>";
?>

注意事项

  • 返回值处理:注意区分 false(验证失败)、null(空值或不存在)和实际值
  • 数组过滤:使用 FILTER_REQUIRE_ARRAY 标志处理数组输入
  • 性能优化:对于大型数据集,考虑分批处理以避免内存问题
  • 错误处理:始终检查函数是否返回 false(表示处理失败)
  • 自定义回调:使用 FILTER_CALLBACK 可以实现复杂的自定义验证逻辑
  • 默认值:通过 options 中的 default 键可以设置验证失败时的默认值
  • 嵌套数组:函数本身不支持深度嵌套数组,需要递归处理
  • 安全性:过滤器不能完全替代其他安全措施,如SQL参数化查询和输出编码

与相关函数的比较

方面 filter_var_array() filter_input_array() filter_var()
数据来源 直接传递的数组 输入源(GET/POST/COOKIE等) 单个变量
处理数量 批量处理多个值 批量处理多个输入变量 单个值
配置复杂性 需要定义过滤器配置数组 需要定义过滤器配置数组 简单参数传递
适用场景 处理内存中的数据数组 处理用户输入数据 单个值验证
性能 批量处理效率高 批量处理效率高 适合简单场景
灵活性 高度灵活,支持复杂配置 高度灵活,支持复杂配置 简单直接

最佳实践

  1. 始终验证返回值:检查函数是否返回 false 表示处理失败
  2. 使用明确的配置:为每个字段指定具体的过滤器和选项
  3. 处理数组数据:对于复选框等多选数据,使用 FILTER_REQUIRE_ARRAY 标志
  4. 设置默认值:通过 options 中的 default 键提供安全的默认值
  5. 组合使用:将过滤器验证与自定义验证逻辑结合使用
  6. 错误信息:提供清晰的错误信息,帮助用户修正输入
  7. 性能监控:对于大型数据集,监控处理时间和内存使用
  8. 安全输出:即使数据已经过滤,输出到HTML时仍要使用 htmlspecialchars()

浏览器支持

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