Skip to content

PHP Tutorial PHP Security

Input Validation

Lesson 296 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Input validation means checking that data is what you expect before you use it. It will not replace escaping or prepared statements, but it removes whole classes of problems and keeps your data clean.

What to check

CheckExample
PresenceRequired fields are not empty
TypeA number really is a number
LengthNames are 1 to 100 characters
RangeQuantity is 1 to 99
FormatEmail, date, postcode, username pattern
Allowed valuesStatus is one of draft, published
ConsistencyEnd date is after the start date

Allow-list, not block-list

Decide what is allowed, and reject the rest. A block-list (“no <script>, no DROP“) always misses something.

index.php
PHP
<?php
function validateStatus(string $status): string
{
    $allowed = ["draft", "published", "archived"];

    if (!in_array($status, $allowed, true)) {
        throw new InvalidArgumentException("Invalid status");
    }

    return $status;
}

echo validateStatus("published") . "\n";

try {
    validateStatus("published' OR 1=1 --");
} catch (InvalidArgumentException $e) {
    echo $e->getMessage();
}
Output
OUTPUT
published
Invalid status

Validate type, not just content

Users (and attackers) can send arrays where you expect strings.

index.php
PHP
<?php
function inputString(mixed $value, int $max = 255): ?string
{
    if (!is_string($value)) {
        return null;
    }

    $value = trim($value);

    return ($value !== "" && mb_strlen($value) <= $max) ? $value : null;
}

var_dump(inputString("  Ana "));
var_dump(inputString(["x"]));          // name[]=x
var_dump(inputString(str_repeat("a", 300)));
Output
OUTPUT
string(3) "Ana"
NULL
NULL

Numbers

index.php
PHP
<?php
function inputInt(mixed $value, int $min, int $max): ?int
{
    $int = filter_var($value, FILTER_VALIDATE_INT, ["options" => ["min_range" => $min, "max_range" => $max]]);

    return $int === false ? null : $int;
}

var_dump(inputInt("5", 1, 10));
var_dump(inputInt("5; DROP TABLE", 1, 10));
var_dump(inputInt("0", 0, 10));
Output
OUTPUT
int(5)
NULL
int(0)

Reject, do not “repair”

If the input is wrong, say so. Silently cleaning it (“removing bad characters”) can change the meaning, and attackers can exploit the cleaning code. Normal trimming and case folding are fine.

Validate on the server, always

JavaScript validation and the HTML required or pattern attributes are for user comfort. Anyone can skip them. The server’s check is the real one.

Validate early, near the edge

Convert raw request data into validated, typed values as soon as it arrives. The rest of the program then works with clean data (an int, a Status enum, a DateTimeImmutable) rather than strings.

index.php
PHP
<?php
enum Status: string
{
    case Draft = "draft";
    case Published = "published";
}

$input = ["status" => "published", "page" => "3"];

$status = Status::tryFrom((string) ($input["status"] ?? ""));
$page = max(1, (int) ($input["page"] ?? 1));

echo ($status?->name ?? "invalid") . ", page $page";
Output
OUTPUT
Published, page 3

Limits on size

Limit the length of every text field, the number of items in an array and the size of uploads and request bodies. Unlimited input is a denial-of-service risk.

Validation is not enough

A perfectly valid name can be <script>alert(1)</script>. Valid does not mean safe. You still need:

  • Prepared statements for SQL.
  • Output escaping for HTML.

Each protection has its own job.

lightbulb
Message wording
Tell users how to fix a problem (“Use 10 to 100 characters”), not only that one exists. Do not echo back dangerous input in the message without escaping it.