Sanitising form input

Build a once-and-reuse sanitisation pipeline for form fields — trim, strip tags, lowercase emails, default missing values — then apply the same pipeline across every form in the app by composition.

Functions used

The scenario

Every form in the app needs the same handful of defences: trim whitespace, strip unsafe tags, normalise email case, default missing fields. The per-route code usually ends up copy-pasted and drifts out of sync.

Build one field-level sanitiser and one form-level sanitiser. Reuse both everywhere.

Field-level — a small composed callable per kind of field

use PinkCrab\FunctionConstructors\GeneralFunctions as F;
use PinkCrab\FunctionConstructors\Arrays;
use PinkCrab\FunctionConstructors\Strings as Str;
use PinkCrab\FunctionConstructors\Comparisons as C;

// Plain text: trim + strip tags.
$sanitiseText = F\compose(
    Str\trim(" \t\n\r\0\x0B"),   // includes spaces
    Str\stripTags()
);

// Email: trim + strip tags + lowercase.
$sanitiseEmail = F\compose(
    $sanitiseText,
    'strtolower'
);

// Any required field that came back empty gets a default.
$defaultTo = fn($value) => F\ifThen(
    F\compose(C\not(C\notEmpty)),   // predicate: empty?
    F\always($value)
);

Each is a first-class callable you can pass around, test, or drop into a pipeline.

Form-level — map fields-to-sanitisers over the input

// Configuration: one row per field.
$rules = [
    'name'    => $sanitiseText,
    'email'   => $sanitiseEmail,
    'message' => F\compose($sanitiseText, $defaultTo('(no message supplied)')),
];

// Apply every rule to the corresponding input field.
$sanitiseForm = function (array $input) use ($rules): array {
    $out = [];
    foreach ($rules as $field => $sanitiser) {
        $out[$field] = $sanitiser($input[$field] ?? '');
    }
    return $out;
};

Use it anywhere

$submitted = [
    'name'    => '  <b>Ada</b> ',
    'email'   => ' ADA@Example.com ',
    'message' => '',
];

$clean = $sanitiseForm($submitted);
/*
[
    'name'    => 'Ada',
    'email'   => 'ada@example.com',
    'message' => '(no message supplied)',
]
*/

Adding a rule for a new form

Add one more row to $rules. Same sanitiser pipeline if the field is just text — or compose a specific one if it’s different:

$rules['username'] = F\compose(
    $sanitiseText,
    'strtolower',
    Str\trim('@')                    // allow users to paste '@ada' — strip the sigil
);

Why this is better than htmlspecialchars on every write

It isn’t a single “one and done” call — it’s a named pipeline per field kind. You can test $sanitiseEmail in isolation, extend it (add punycode normalisation, say) in one place, and reuse it across forms without copying.