PHP Filters Advanced

Su Subhalakshmi Chandran Updated 17 Oct 2026
6 min read ·Lesson 71 of 248
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 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 User-defined Functions Slicing Strings Function Arguments 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 AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions PHP Superglobals $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE PHP Type Juggling MySQL Order By MySQL Delete Data MySQL Update Data MySQL Limit Data MySQL Joins with PHP Pagination with PHP PDO Introduction PDO Connect PDO Prepared Statements PDO Fetch Modes PSR-4 Autoloading Installing Extensions (PECL / PIE) OPcache & Performance PHP-FPM Project: Contact Form with Email Project: Login & Registration System Project: CRUD App with MySQL Project: Shopping Cart with Sessions Project: REST API with PHP DateTime Class Timezones PHP Include Include vs Require PHP File Handling PHP File Open/Read PHP File Create/Write File Delete / Rename / Copy PHP Directories PHP CSV Files
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.