Skip to content
Carl Victor Fontanos
Carl Victor Fontanos

Carl Victor Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

array_intersect_key: The Input Whitelist Hiding in the Standard Library

C
Carlo Fontanos
· 2 min read

The bug class is old enough to drink: a form handler does $user->update($_POST) or builds an UPDATE from request keys, and someone adds role=admin to the POST body with DevTools. Mass assignment. The fix everyone knows is "whitelist your fields" - the fix nobody remembers is that PHP has a one-liner for it:

$allowed = ['name', 'email', 'bio', 'website'];

$data = array_intersect_key($_POST, array_flip($allowed));
// Only whitelisted keys survive. role=admin? Gone, silently.

array_intersect_key keeps entries from the first array whose keys exist in the second; array_flip turns your list of allowed names into the key-shaped comparison array it wants. Ten seconds to write, trivially auditable in review - the entire filter is visible in the $allowed literal.

The complement: array_diff_key for stripping

// Blocklist shape - remove what must never pass through
$safe = array_diff_key($row, array_flip(['password_hash', 'remember_token', 'totp_secret']));
echo json_encode($safe);

Same trick inverted: keep keys not in the second array. Right for API responses where you're redacting known-sensitive columns from otherwise-fine rows. (General rule stands: whitelists for input, because you know what you accept; blocklists only for output redaction, where the row shape is yours.)

Building the full input pipeline

$defaults = ['bio' => '', 'website' => null];

$data = array_intersect_key($_POST, array_flip($allowed))   // 1. whitelist
      + $defaults;                                          // 2. fill gaps (union keeps left)

$data = array_map(fn ($v) => is_string($v) ? trim($v) : $v, $data);   // 3. normalize

// 4. validate values - whitelisting KEYS says nothing about VALUES
$errors = validate($data, [
    'email' => fn ($v) => filter_var($v, FILTER_VALIDATE_EMAIL),
    'website' => fn ($v) => $v === null || filter_var($v, FILTER_VALIDATE_URL),
]);

Step 4 is the honesty clause: intersect_key controls which fields exist, not what's in them. Key filtering plus value validation together are what frameworks' validated() helpers do - and this is exactly what to write when you're in vanilla PHP, a webhook endpoint, or a WordPress handler where the framework isn't holding your hand.

Related key-fu worth knowing

  • array_key_first()/array_key_last() - no more reset()/end() pointer dances.
  • array_is_list($arr) (8.1) - distinguishes ['a','b'] from ['x'=>'a'] before you JSON-encode something that must be an array, not an object.
  • array_filter($arr, $fn, ARRAY_FILTER_USE_KEY) - predicate-based key filtering when the whitelist is a pattern (e.g., keys starting with 'meta_') rather than a list.

Every framework wraps this in something (Laravel's $request->only()); knowing the primitive means you can build the safe path anywhere - and recognize its absence in code review, which is where this one-liner earns its keep.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Message sent!

Your details are only used to reply to you.

Keep reading