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.
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.
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.
// 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;
};$submitted = [
'name' => ' <b>Ada</b> ',
'email' => ' ADA@Example.com ',
'message' => '',
];
$clean = $sanitiseForm($submitted);
/*
[
'name' => 'Ada',
'email' => 'ada@example.com',
'message' => '(no message supplied)',
]
*/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
);htmlspecialchars on every writeIt 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.