PHP Filters Advanced

Su Subhalakshmi Chandran Updated 17 Oct 2026
6 min read ·Lesson 72 of 109
In this post

PHP Filters introduced the filter API: filter_var() , filter_var_array() , filter_input() , the options structure, default , FILTER_CALLBACK , and array handling. This lesson goes deeper into the flag

PHP Filters introduced the filter API: filter_var(), filter_var_array(), filter_input(), the options structure, default, FILTER_CALLBACK, and array handling. This lesson goes deeper into the flags and options of individual filters — number formats, IP address ranges, host names, low-level character stripping — and shows how to combine them into a schema that validates a complete request payload.


Integer Flags: Hexadecimal and Octal

By default, FILTER_VALIDATE_INT accepts only decimal notation without leading zeros:

<?php
var_dump(filter_var('0x1A', FILTER_VALIDATE_INT));
var_dump(filter_var('0x1A', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
var_dump(filter_var('0755', FILTER_VALIDATE_INT));
var_dump(filter_var('0755', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));

Output

bool(false)
int(26)
bool(false)
int(493)
  • FILTER_FLAG_ALLOW_HEX accepts 0x-prefixed hexadecimal (0x1A = 26).
  • FILTER_FLAG_ALLOW_OCTAL accepts numbers with a leading 0 as octal (0755 = 493) — typical for Unix file permissions.
  • Without these flags, 0755 is rejected rather than silently interpreted. This is usually what you want for user input: a PIN code or quantity typed with a leading zero should not suddenly become an octal number.

Flags combine with options:

filter_var($input, FILTER_VALIDATE_INT, [
    'flags' => FILTER_FLAG_ALLOW_HEX,
    'options' => ['min_range' => 0, 'max_range' => 0xFFFFFF],
]);

Float Options and Flags

<?php
var_dump(filter_var('1,299.50', FILTER_VALIDATE_FLOAT));
var_dump(filter_var('1,299.50', FILTER_VALIDATE_FLOAT, FILTER_FLAG_ALLOW_THOUSAND));
var_dump(filter_var('12,5', FILTER_VALIDATE_FLOAT, ['options' => ['decimal' => ',']]));
var_dump(filter_var('1e3', FILTER_VALIDATE_FLOAT));
var_dump(filter_var('105.5', FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 0, 'max_range' => 100]]));

Output

bool(false)
float(1299.5)
float(12.5)
float(1000)
bool(false)
SettingEffect
FILTER_FLAG_ALLOW_THOUSANDAccepts thousands separators (, by default)
'decimal' optionSets the decimal separator (for example , for many European formats)
'min_range' / 'max_range' optionsRange check for floats

Scientific notation such as 1e3 is accepted by default. If a form field should accept only plain decimal numbers, add a regular expression check or use FILTER_VALIDATE_REGEXP.


IP Address Flags

<?php
$addresses = ['8.8.8.8', '192.168.1.10', '127.0.0.1', '::1', '2001:4860:4860::8888'];

foreach ($addresses as $ip) {
    $any = filter_var($ip, FILTER_VALIDATE_IP) !== false ? 'ip' : '-';
    $v4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false ? 'v4' : '-';
    $public = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false ? 'public' : '-';

    printf("%-22s %-3s %-3s %s\n", $ip, $any, $v4, $public);
}

Output

8.8.8.8                ip  v4  public
192.168.1.10           ip  v4  -
127.0.0.1              ip  v4  -
::1                    ip  -   -
2001:4860:4860::8888   ip  -   public
FlagRejects
FILTER_FLAG_IPV4 / FILTER_FLAG_IPV6Addresses of the other version
FILTER_FLAG_NO_PRIV_RANGEPrivate ranges such as 10.x.x.x, 172.16–31.x.x, 192.168.x.x (and IPv6 equivalents)
FILTER_FLAG_NO_RES_RANGEReserved ranges such as 127.0.0.0/8 (loopback) and 0.0.0.0/8
FILTER_FLAG_GLOBAL_RANGE (PHP 8.2+)Everything that is not a globally routable address

Why This Matters

When a server fetches URLs or connects to addresses provided by users (webhooks, image imports, link previews), an attacker may supply an internal address such as 127.0.0.1 or 10.0.0.5 to reach services that are not supposed to be public. Checking the resolved IP address with FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE (or FILTER_FLAG_GLOBAL_RANGE) is one of the defenses against this Server-Side Request Forgery (SSRF).


Domain Names: FILTER_VALIDATE_DOMAIN

<?php
foreach (['gocourse.in', 'exa_mple.com', '-bad.example', 'localhost'] as $domain) {
    $basic = filter_var($domain, FILTER_VALIDATE_DOMAIN) !== false ? 'valid' : 'invalid';
    $host = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false ? 'valid' : 'invalid';

    printf("%-14s domain: %-8s hostname: %s\n", $domain, $basic, $host);
}

Output

gocourse.in    domain: valid    hostname: valid
exa_mple.com   domain: valid    hostname: invalid
-bad.example   domain: valid    hostname: invalid
localhost      domain: valid    hostname: valid
  • Without flags, the filter checks only length rules for domain names, which are quite permissive (DNS itself allows underscores in some records).
  • FILTER_FLAG_HOSTNAME applies the stricter rules for host names: letters, digits, and hyphens only, with no hyphen at the start or end of a label. Use it for values that will be used as server or website names.
  • Neither check confirms that the domain exists; use DNS lookups (checkdnsrr(), dns_get_record()) if that is required.

MAC Addresses

<?php
var_dump(filter_var('00:1A:2B:3C:4D:5E', FILTER_VALIDATE_MAC));
var_dump(filter_var('00-1A-2B-3C-4D-5E', FILTER_VALIDATE_MAC, ['options' => ['separator' => ':']]));

Output

string(17) "00:1A:2B:3C:4D:5E"
bool(false)

By default, colons, hyphens, and the dotted Cisco format are accepted. The separator option restricts the separator to one character.


Low-Level Character Flags

FILTER_UNSAFE_RAW (the default filter) changes nothing unless flags are given. Its flags remove or encode control characters and non-ASCII bytes:

<?php
$input = "Hello\x07World\n";

var_dump(filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW));
var_dump(strlen(filter_var("Café", FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH)));

Output

string(10) "HelloWorld"
int(3)
FlagEffect
FILTER_FLAG_STRIP_LOWRemoves characters with ASCII code below 32 (including tabs and newlines)
FILTER_FLAG_STRIP_HIGHRemoves bytes above 127 — this destroys UTF-8 text (here, é disappeared)
FILTER_FLAG_STRIP_BACKTICKRemoves backticks
FILTER_FLAG_ENCODE_LOW / ENCODE_HIGH / ENCODE_AMPEncodes characters as HTML numeric entities instead of removing them

FILTER_FLAG_STRIP_LOW is useful for single-line fields (names, titles) where control characters are never legitimate. Avoid STRIP_HIGH for any text that may contain non-English characters.


Failing Loudly: FILTER_THROW_ON_FAILURE (PHP 8.5+)

PHP 8.5 adds a flag that makes validation filters throw an exception instead of returning false:

<?php
try {
    $age = filter_var('abc', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
} catch (Filter\FilterFailedException $e) {
    echo "Invalid input: " . $e->getMessage() . "\n";
}

This is convenient when invalid input should simply abort processing. On earlier versions, check for false and throw your own exception.


Practical Example: Validating an API Payload With a Schema

<?php
function validatePayload(array $payload): array
{
    $schema = [
        'name' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_LOW],
        'age' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 18, 'max_range' => 120]],
        'server_ip' => ['filter' => FILTER_VALIDATE_IP, 'flags' => FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE],
        'newsletter' => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
        'tags' => [
            'filter' => FILTER_VALIDATE_REGEXP,
            'flags' => FILTER_REQUIRE_ARRAY,
            'options' => ['regexp' => '/^[a-z0-9-]{1,20}$/'],
        ],
    ];

    $data = filter_var_array($payload, $schema);
    $errors = [];

    foreach ($schema as $field => $rule) {
        $value = $data[$field];
        $isBool = $rule['filter'] === FILTER_VALIDATE_BOOL;

        if ($value === null && !array_key_exists($field, $payload)) {
            $errors[] = "$field: missing";
        } elseif ($isBool ? $value === null : $value === false) {
            $errors[] = "$field: invalid";
        } elseif (is_array($value)) {
            foreach ($value as $index => $item) {
                if ($item === false) {
                    $errors[] = "{$field}[$index]: invalid";
                }
            }
        }
    }

    return [$data, $errors];
}

[$data, $errors] = validatePayload([
    'name' => "Asha\x00",
    'age' => '17',
    'server_ip' => '10.0.0.5',
    'newsletter' => 'maybe',
    'tags' => ['php', '<b>x</b>'],
]);

echo implode("\n", $errors) . "\n";
var_dump($data['name']);

Output

age: invalid
server_ip: invalid
newsletter: invalid
tags[1]: invalid
string(4) "Asha"

Step-by-Step Explanation

  1. The schema describes every field with a filter, flags, and options — one place to read and change the rules.
  2. filter_var_array() applies all filters at once.
  3. The error loop distinguishes three situations:
    • missing fields (null and absent from the payload),
    • invalid values (false, or null for the boolean filter, which uses FILTER_NULL_ON_FAILURE because false is a valid result),
    • invalid elements in arrays, reported with their index.
  4. The name's invisible NUL byte (\x00) is stripped by FILTER_FLAG_STRIP_LOW.
  5. The private IP address, the out-of-range age, the ambiguous maybe, and the HTML tag in the tags list are all reported.

For large applications, validation libraries or framework validators offer richer rules and messages, but they rely on the same building blocks.


Common Mistakes

Enabling Octal or Hex Unintentionally

Effect: User input like 010 becomes 8.

Fix: Use FILTER_FLAG_ALLOW_OCTAL/ALLOW_HEX only where these formats are expected.

Validating User-Provided IPs Without Range Flags

Effect: Internal services can be targeted through your server (SSRF).

Fix: Reject private and reserved ranges for addresses your server connects to.

Using STRIP_HIGH on Text

Effect: Non-English characters are removed.

Fix: Use STRIP_LOW only, or no flags at all.

Treating false as Failure for Boolean Filters

Effect: A valid "no" answer is reported as invalid.

Fix: Use FILTER_NULL_ON_FAILURE and check for null.

Confusing Domain and Host Name Rules

Effect: Invalid host names such as exa_mple.com are accepted.

Fix: Add FILTER_FLAG_HOSTNAME.


Best Practices

  • Choose flags deliberately for each filter; defaults are strict for numbers and permissive for domains.
  • Protect server-side connections by rejecting private and reserved IP ranges.
  • Use FILTER_NULL_ON_FAILURE for boolean input.
  • Describe payload validation as a schema and report errors per field (and per array element).
  • On PHP 8.5+, consider FILTER_THROW_ON_FAILURE where invalid input should stop processing.

Official reference: https://www.php.net/manual/en/filter.filters.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators
Ask about this post
AI Ask about this post

Ask questions about PHP Filters Advanced and get answers drawn from it.

Signed-in readers only.