PHP 高级过滤器

Batch filtering with filter_var_array, FILTER_CALLBACK, and nested structures.

Intermediate
Key Points`filter_var_array` declares a whole form's rules in one place.

Batch filtering

PHP
<?php
$data = [
    "name"  => "Tom",
    "age"   => "25",
    "email" => "tom@example.com",
    "ip"    => "192.168.1.1",
    "site"  => "not a url",
];

$rules = [
    "name"  => ["filter" => FILTER_SANITIZE_SPECIAL_CHARS],
    "age"   => ["filter" => FILTER_VALIDATE_INT, "options" => ["min_range" => 18]],
    "email" => FILTER_VALIDATE_EMAIL,
    "ip"    => FILTER_VALIDATE_IP,
    "site"  => FILTER_VALIDATE_URL,
];

print_r(filter_var_array($data, $rules));
Output
Array
(
    [name] => Tom
    [age] => 25
    [email] => tom@example.com
    [ip] => 192.168.1.1
    [site] => 
)

site became an empty string (false cast to string). That is dangerous: failure is indistinguishable from empty. Add FILTER_NULL_ON_FAILURE to tell them apart.

PHP
<?php
$rules = [
    "site" => [
        "filter" => FILTER_VALIDATE_URL,
        "flags"  => FILTER_NULL_ON_FAILURE,
    ],
];

$r = filter_var_array(["site" => "not a url"], $rules);
var_dump($r["site"]);
var_dump(array_key_exists("site", $r));
Output
NULL
bool(true)

Required and missing

Missing keys come back as null without error. To distinguish missing from invalid, check array_key_exists first.

PHP
<?php
$input = ["email" => "bad@"];
$rules = ["email" => FILTER_VALIDATE_EMAIL, "age" => FILTER_VALIDATE_INT];

$clean = filter_var_array($input, $rules);
$errors = [];

foreach ($rules as $field => $_) {
    if (!array_key_exists($field, $input) || $input[$field] === "") {
        $errors[$field] = "$field 必填";
    } elseif ($clean[$field] === null || $clean[$field] === false) {
        $errors[$field] = "$field 格式不正确";
    }
}

print_r($errors);
Output
Array
(
    [email] => email 格式不正确
    [age] => age 必填
)

FILTER_CALLBACK

When built-ins fall short, FILTER_CALLBACK attaches your own function — good for normalisation like lowercasing or whitespace collapsing.

PHP
<?php
function normalize(string $v): string {
    return mb_strtolower(preg_replace("/\s+/u", " ", trim($v)));
}

$r = filter_var("  Hello   World  ", FILTER_CALLBACK, ["options" => "normalize"]);
echo $r, "\n";

// 用闭包
$slug = filter_var("Hello World 2023", FILTER_CALLBACK, [
    "options" => fn($v) => strtolower(str_replace(" ", "-", $v)),
]);
echo $slug, "\n";
Output
hello world
hello-world-2023
FILTER_CALLBACK never receives null — PHP returns null without calling. Handle null separately.

Nested arrays

filter_var_array handles one level. For deeper structures recurse or filter level by level.

PHP
<?php
$input = [
    "user" => ["name" => " Tom ", "email" => "tom@example.com"],
    "tags" => ["  php ", "web", ""],
];

function cleanValue(mixed $v): mixed {
    if (is_array($v)) {
        return array_map("cleanValue", $v);
    }
    return is_string($v) ? trim($v) : $v;
}

$clean = cleanValue($input);
print_r($clean);

$clean["tags"] = array_values(array_filter($clean["tags"], fn($t) => $t !== ""));
print_r($clean["tags"]);
Output
Array
(
    [user] => Array
        (
            [name] => Tom
            [email] => tom@example.com
        )

    [tags] => Array
        (
            [0] => php
            [1] => web
            [2] => 
        )

)
Array
(
    [0] => php
    [1] => web
)

A whole-form validator

PHP
<?php
function validateForm(array $in): array
{
    $rules = [
        "name"  => ["required" => true, "filter" => FILTER_SANITIZE_SPECIAL_CHARS],
        "email" => ["required" => true, "filter" => FILTER_VALIDATE_EMAIL],
        "age"   => ["required" => true, "filter" => FILTER_VALIDATE_INT,
                    "options" => ["min_range" => 18, "max_range" => 99]],
        "site"  => ["required" => false, "filter" => FILTER_VALIDATE_URL],
    ];

    $clean = filter_var_array($in, $rules);
    $errors = [];

    foreach ($rules as $field => $r) {
        $raw = trim((string)($in[$field] ?? ""));

        if ($r["required"] && $raw === "") {
            $errors[$field] = "$field 必填";
            continue;
        }
        if ($raw === "") {
            continue;
        }
        if ($clean[$field] === false || $clean[$field] === null) {
            $errors[$field] = "$field 格式不正确";
        }
    }

    return [$errors, $clean];
}

[$errors, $clean] = validateForm([
    "name" => "Tom", "email" => "tom@example.com", "age" => "17", "site" => "",
]);
print_r($errors);
print_r($clean);
Output
Array
(
    [age] => age 格式不正确
)
Array
(
    [name] => Tom
    [email] => tom@example.com
    [age] => 
    [site] => 
)

The rule table declares required and format in one place, so adding a field touches only the array. This is the core idea behind Laravel's and Symfony's validators.

filter_var_array ignores extra keys like required and only reads filter / flags / options. Pass the whole rule table directly — extracting just filter would drop the options range checks.

Friends & Partners