Lesson 93 of 100
Validation15 min5 checksPHP 8.5

Normalize Inputs Before Validation

Learn to trim, fold and convert raw input into a predictable shape, then validate it with filter_var() and return either a clean record or structured errors.

01 · THE IDEAREAD → TRY → LEARN

Why normalize first?

Raw input is messy. A user pastes an email with a trailing space, types ADA@Example.COM, or sends an age as the string " 36 ". If you validate that text as-is, you reject data that a person would call correct. If you store it as-is, you end up with duplicates that differ only by case or spacing.

The usual pipeline has three steps:

  1. Read each field defensively. A missing key or a non-scalar value becomes an empty string.
  2. Normalize it: trim, collapse whitespace, fold case, and so on.
  3. Validate the normalized value and return either a clean record or a list of errors.

Normalization should be predictable and should never invent data. Trimming and lowercasing an ASCII email is safe. Guessing what a malformed value "probably meant" is not.

Whitespace

trim() removes whitespace from both ends. For internal runs of spaces, tabs and newlines, preg_replace('/\s+/', ' ', $text) collapses each run into one space. Do the trim first so no leading or trailing space survives.

Example
<?php
$raw = "  Ada \t  Lovelace\n";
echo json_encode(preg_replace('/\s+/', ' ', trim($raw))), PHP_EOL;
Output
"Ada Lovelace"

The output is the string "Ada Lovelace".

Validating with filter_var()

filter_var($value, $filter, $options) returns the filtered value on success and false on failure. Scalars are converted to strings internally before filtering, so 42 and '42' behave the same. For integers, pass min_range and max_range inside the nested options key:

Example
<?php
$options = ['options' => ['min_range' => 1, 'max_range' => 120]];
echo json_encode([
    filter_var('42', FILTER_VALIDATE_INT, $options),
    filter_var('4x', FILTER_VALIDATE_INT, $options),
    filter_var('200', FILTER_VALIDATE_INT, $options),
]), PHP_EOL;
Output
[42,false,false]

The valid value comes back as a real int. The other two give false. This is better than an (int) cast, because (int) '4x' silently becomes 4.

Be careful with the failure value. A validated integer 0 is falsy, so compare with === false rather than testing truthiness.

Worked example: a tag normalizer

This function normalizes a tag and then validates it. It trims, lowercases, turns whitespace runs into hyphens, and checks the result against a small pattern.

Example
<?php
function normalize_tag(string $raw): array
{
    $tag = strtolower(trim($raw));
    $tag = preg_replace('/\s+/', '-', $tag);

    if ($tag === '') {
        return ['ok' => false, 'error' => 'required'];
    }

    if (preg_match('/^[a-z0-9-]{1,20}$/', $tag) !== 1) {
        return ['ok' => false, 'error' => 'invalid'];
    }

    return ['ok' => true, 'value' => $tag];
}

$results = array_map('normalize_tag', ["  PHP   Basics ", "   ", "C++"]);
echo json_encode($results), PHP_EOL;
Output
[{"ok":true,"value":"php-basics"},{"ok":false,"error":"required"},{"ok":false,"error":"invalid"}]

Notice the order: normalization runs before the emptiness check. A whitespace-only string is therefore reported as required, not invalid. The function also always returns the same shape, with an ok flag and either value or error, so callers never have to guess.

Exercise contract

Write normalize_signup(array $input): array. It reads name, email and age from the input. A missing key or a non-scalar value counts as an empty string.

Normalization:

  • name: trim, then collapse each whitespace run to one space.
  • email: trim, then lowercase with strtolower().
  • age: trim, then convert to an integer with filter_var().

Validation:

  • name must not be empty. If it is, the error is required.
  • email must pass FILTER_VALIDATE_EMAIL. If it does not, the error is invalid. An empty email also fails this way.
  • age must be an integer from 13 to 120 inclusive. If it is not, the error is invalid.

If any field fails, return ['ok' => false, 'errors' => [...]]. The errors array is keyed by field and lists failing fields in the order name, email, age. Otherwise return ['ok' => true, 'value' => ['name' => ..., 'email' => ..., 'age' => ...]], where age is an int.

The tests compare the returned arrays, so build them with exactly these keys and in this order.

Recap

  • Normalize first, then validate the normalized value.
  • Use trim() and preg_replace() for whitespace and strtolower() for ASCII case folding.
  • Use filter_var() with === false checks, not casts, to turn text into validated values.
  • Return one consistent shape: a clean record, or structured errors keyed by field.
Before you start

Functions · Arrays · Types

You’ll practice
  • Return a clear in-memory validated record or structured errors
  • Implement and test a deterministic PHP 8.4 function
Your task

Make normalize_signup() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.