skip to content

Validation & Filtering

filter_var and filter_input check a value against a filter such as FILTER_VALIDATE_INT and return it typed or signal failure. Interviewers ask how to validate and why sanitising is not escaping.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

6

In PHP, how do you validate an age field with filter_var() and FILTER_VALIDATE_INT, and how do you detect a failed check correctly?

level: juniorimportance: must knowfreq 66%

answer

  1. typed int back, or false
  2. min_range and max_range in options
  3. 0 is a valid, falsy result
  4. compare with === false
  5. leading zero rejected without ALLOW_OCTAL

basics

~10 s

Call filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => 16, 'max_range' => 99]]). It returns an int on success and false on failure, so test with === false, because a valid 0 is falsy.

solid answer

~40 s

`filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 16, 'max_range' => 99]])` checks that the whole string is an integer inside the range and returns it as an `int`, or `false` when it is not. Surrounding whitespace is trimmed and a leading `+` or `-` is accepted, but `"18.5"`, `"18abc"`, an empty string and `"018"` all fail; a leading zero is rejected unless `FILTER_FLAG_ALLOW_OCTAL` is set. Because `0` is a legitimate result and is falsy, the failure test must be `=== false`, never `!$age`. After the check, use the returned `int` rather than the raw `$_POST` string. This is stricter than an `(int)` cast, which turns `"18abc"` into `18` silently, and than `is_numeric()`, which accepts `"18.5"` and `"1e3"`.

code

php · 12 lines
php
<?php
declare(strict_types=1);

$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT, [
    'options' => ['min_range' => 16, 'max_range' => 99],
]);

if ($age === false) {            // not !$age: 0 is a valid int for other ranges
    $errors['age'] = 'Enter a whole number from 16 to 99.';
} else {
    $volunteer['age'] = $age;    // an int from here on, not the raw string
}

go deeper

for a junior

Recall the filter_var() call with FILTER_VALIDATE_INT and a min_range and max_range, that it returns an int or false, and that the check is === false.

for a middle

Explain the exact acceptance rules: trimming, signs, leading zeros, range options, scalar-only input, and why a cast or is_numeric() is not validation.

for a senior

Show how you structure form validation so the typed result is the only value used afterwards, and how you report missing, malformed and out-of-range input distinctly.

for a principal

Decide where validation lives in the codebase so every entry point applies the same rules, and when a shared validation layer beats per-script filter_var() calls.

## What FILTER_VALIDATE_INT does `filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed` runs one filter over one value. With `FILTER_VALIDATE_INT` it answers a yes-or-no question, *is this value a whole integer?*, and on "yes" it hands back the value **converted to `int`**. On "no" it returns `false` by default. The input is treated as a string (an `int` argument is converted to its string form first), and the filter then applies these rules: 1. Leading and trailing whitespace (space, tab, CR, LF, vertical tab) is trimmed. 2. An empty string fails. 3. One leading `+` or `-` is allowed. 4. Every remaining character must be a decimal digit, and the number must fit in PHP's `int`. 5. A leading `0` followed by more digits fails, unless `FILTER_FLAG_ALLOW_OCTAL` is set; `FILTER_FLAG_ALLOW_HEX` similarly enables `0x` prefixes. 6. If `min_range` or `max_range` is given, the number must lie inside the range. ## Passing options and flags The third argument is either a flags integer or an array with two optional keys: - `'options'`: an array of filter-specific settings, here `min_range`, `max_range` and `default` (a value returned instead of the failure result); - `'flags'`: a bitmask such as `FILTER_FLAG_ALLOW_HEX` or `FILTER_NULL_ON_FAILURE`. For a volunteer sign-up form that accepts ages 16 to 99: `$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 16, 'max_range' => 99]]);` ## Detecting failure: the zero trap `filter_var()` returns the validated value itself. For `FILTER_VALIDATE_INT` that value can be `0`, and `0` is falsy. So: - `if (!$age)` treats a valid `0` as a failure, and in other fields treats a real failure and a real zero identically; - `if ($age === false)` distinguishes them exactly. With a range that excludes 0 the bug hides, then appears when someone reuses the pattern for a quantity or an offset. Always compare strictly with the failure value. ## How it compares with the alternatives | Input | `FILTER_VALIDATE_INT` | `(int)` cast | `is_numeric()` | |---|---|---|---| | `"18"` | `18` | `18` | true | | `" 18 "` | `18` | `18` | true | | `"18abc"` | `false` | `18` (silent) | false | | `"18.5"` | `false` | `18` | true | | `"1e3"` | `false` | `1000` | true | | `"018"` | `false` | `18` | true | | `""` | `false` | `0` | false | The cast never fails, so it cannot validate. `is_numeric()` validates "some number", including floats and exponent notation, and still leaves the value a string. `FILTER_VALIDATE_INT` both rejects and converts, which is what a form field needs. ## Arrays and missing fields - If the client submits `age[]=18`, `$_POST['age']` is an array. `filter_var()` requires a scalar by default and returns `false` rather than a warning or a type error. - A missing field has to be handled before the call: `$_POST['age'] ?? ''` turns "missing" into an empty string, which fails validation. When "missing" and "invalid" need different messages, check `isset()` first or use the array-validation functions, which report missing keys as `null`. ## Leading zeros and identifiers The leading-zero rule makes `FILTER_VALIDATE_INT` the wrong tool for values that only look numeric, such as postcodes, PINs or phone extensions, where `"01234"` is valid and the zero matters. Those are strings of digits, not integers; a digit-only check fits them better. ## After validation Use the returned `int` from then on and leave `$_POST['age']` alone. Validating one variable and then using another is how a checked value and a used value drift apart. Validation also says nothing about how the value is later written into HTML or SQL; that is the job of escaping and parameter binding at the point of use. ## A checklist for integer fields - Decide the real range and pass it as `min_range` and `max_range`; an age of `-3` or `4000` is a valid integer but not a valid age. - Handle "missing" before or alongside "invalid", so the user sees "required" rather than "not a number". - Compare the result with `=== false` (or `=== null` if you add `FILTER_NULL_ON_FAILURE`). - Store and pass on the returned `int`, and type the receiving property or parameter as `int` so a string can never slip through later. - Keep digit-string identifiers out of the integer filter altogether. - Write one test per boundary: the minimum, the maximum, one below, one above, an empty string and an array.

  • Why does filter_var('018', FILTER_VALIDATE_INT) return false?
    The integer filter rejects a leading zero followed by more digits, because in PHP source code such a literal would be octal. Set `FILTER_FLAG_ALLOW_OCTAL` to accept it as octal, or, for values like postcodes where the zero is meaningful, validate them as digit strings instead of integers.
  • What does FILTER_VALIDATE_INT return when the posted field is an array such as age[]=18?
    `false`. `filter_var()` requires a scalar unless you pass `FILTER_REQUIRE_ARRAY` or `FILTER_FORCE_ARRAY`, so an array input fails validation instead of raising a warning or a type error. The strict `=== false` check therefore also covers clients that tamper with field names.
  • When is a default option useful with FILTER_VALIDATE_INT?
    For optional parameters with a safe fallback, such as a page number: `['options' => ['default' => 1, 'min_range' => 1]]` returns 1 whenever the input fails. For required fields it hides errors the user should see, so reserve it for values where silently falling back is the intended behaviour.

saying these in an interview costs you the question

  • if (!$age) is a safe way to detect a failed filter_var() check
  • An (int) cast validates that the input was a number
  • is_numeric() guarantees the value is an integer
  • FILTER_VALIDATE_INT returns the original string when it succeeds
  • filter_var() throws a TypeError when the field is an array
open as a page

In PHP's filter extension, what separates FILTER_VALIDATE_* from FILTER_SANITIZE_* filters, and why is sanitizing input no substitute for escaping output?

level: middleimportance: must knowfreq 55%

basics

~20 s

Validate filters decide whether a value is acceptable and return it typed or signal failure; sanitize filters strip or encode characters and always return a string. Sanitizing cannot know the output context, so escaping still happens at use.

open as a page

In PHP, when do you reach for ctype_digit() or an allow-list instead of filter_var(), and what traps do the ctype_* functions hide?

level: middleimportance: should knowfreq 38%

basics

~20 s

Use ctype_digit() for digit-only strings where leading zeros matter, such as postcodes, and an allow-list for fixed sets of values. ctype functions take strings: an int argument is deprecated and misread as a character code.

open as a page

In PHP, how do FILTER_NULL_ON_FAILURE and PHP 8.5's FILTER_THROW_ON_FAILURE change what a failed filter_var() check returns?

level: middleimportance: should knowfreq 40%

basics

~10 s

By default a failed filter returns false. FILTER_NULL_ON_FAILURE returns null instead, which matters when false is a valid result; PHP 8.5's FILTER_THROW_ON_FAILURE throws Filter\FilterFailedException. The two flags cannot be combined.

open as a page

In PHP, what do FILTER_VALIDATE_EMAIL and FILTER_VALIDATE_URL actually check on a sign-up form, and what do they still let through?

level: middleimportance: should knowfreq 45%

basics

~10 s

Both check syntax only. FILTER_VALIDATE_EMAIL confirms an address looks well-formed, not that it exists. FILTER_VALIDATE_URL requires a scheme but accepts any scheme, including file: and javascript: forms, so allow-list http and https yourself.

open as a page

When validating a whole PHP form with filter_input_array() or filter_var_array() and a definition array, what does the result contain, and which pitfalls must you handle?

level: seniorimportance: should knowfreq 32%

basics

~20 s

The result holds only the defined keys: a typed value if valid, false if invalid, null if missing. Pitfalls: null-on-failure blurs invalid and missing, arrays fail scalar filters, and filter_input_array() reads the original request input, not $_POST.

open as a page