In PHP, how do HTML form field names become $_POST keys, and what do name[] and name[key] produce?
answer
- the name attribute, not the id
- values always arrive as strings
- name[] appends like $a[]
- repeated plain name: last pair wins
- multi-select without [] loses options
basics
~20 sEach submitted control's name attribute becomes a $_POST key holding a string. A name ending in [] appends to a list, name[key] sets that key, and a repeated plain name keeps only its last value, so multi-selects need name[].
solid answer
~40 sBefore the script runs, PHP parses the POST body into `$_POST`, using each control's `name` attribute as the key; the `id` plays no part, and a control with no name or a `disabled` one is never sent. Every value is a **string**, even from a number input, so `ward=4` arrives as `"4"`. Brackets build arrays with the same logic as PHP's own array syntax: `topics[]` appends at the next integer index, `resident[name]` writes the key `name`, and they nest (`answers[q1][]`). A plain name that appears twice simply overwrites itself, which is why `<select multiple name="district">` delivers only the last selected option; name it `district[]`. Because the client controls the names, read defensively: `$_POST['topics'] ?? []`, and check `is_array()` or `is_string()` before use.
code
php · 18 lines<?php
declare(strict_types=1);
// parse_str() applies the same name-to-key rules that fill $_POST
$body = 'resident[name]=Ana&resident[ward]=4'
. '&topics[]=parks&topics[]=transit'
. '&district=north&district=south';
parse_str($body, $post);
var_dump($post['resident']['ward']); // string(1) "4"
var_dump($post['topics']); // [0 => 'parks', 1 => 'transit']
var_dump($post['district']); // string(5) "south" - last pair wins
// Reading a checkbox group defensively
$topics = $_POST['topics'] ?? [];
if (!is_array($topics)) {
$topics = [];
}go deeper
Recall that the name attribute becomes the key, values are strings, and name[] or name[key] turn a field into an array. Know that a multi-select needs brackets.
Explain the last-pair-wins rule for repeated plain names, how nested brackets map to nested arrays, and why a key can be absent entirely.
Show that request shape is attacker-controlled: an array where a string was expected throws TypeError in PHP 8, so handlers check shape before using values.
Argue for one input-mapping layer that turns $_POST into typed objects, so no handler reads raw superglobals and shape checks live in one place.
## From a submitted form to `$_POST` When a browser submits a form with `method="post"` and the default `application/x-www-form-urlencoded` encoding (or `multipart/form-data`), it sends each **successful control** as a `name=value` pair. PHP parses that body during request startup, before the first line of your script executes, and stores each pair in the superglobal array `$_POST`. The key is always the control's **`name` attribute**. The `id`, the `<label>` text and the placeholder are never sent. Which controls count as successful is decided by the browser: - text inputs, textareas, hidden inputs and selected radio buttons send their value, even when it is empty (`""`); - a checkbox or an `<option>` is sent only when it is checked or selected; - a submit button is sent only if it has a name and is the one that was clicked; - a control with no `name`, or with the `disabled` attribute, is not sent at all. ## Plain names: one key, one string A field named `ward` becomes `$_POST['ward']`. Its value is a **string**, whatever the input type: a `type="number"` field holding 4 arrives as `"4"`, not `int(4)`. Converting and validating it is your job. If two pairs share the same plain name, PHP writes the second over the first: the **last pair wins**. The body `district=north&district=south` leaves `$_POST['district'] === 'south'`, with no warning. ## Brackets build arrays PHP reads square brackets in a field name the way it reads them in code: | `name` attribute | Resulting value in `$_POST` | |---|---| | `topics[]` (on several checkboxes) | `$_POST['topics']` is a list: keys `0`, `1`, … in document order | | `resident[name]` and `resident[ward]` | `$_POST['resident']` is `['name' => ..., 'ward' => ...]` | | `score[3]` | integer key `3` (a canonical decimal string becomes an int key) | | `answers[q1][]` | a nested list under `$_POST['answers']['q1']` | | `district` repeated, no brackets | a single string, the last value sent | So `[]` behaves like `$a[] = $v` (append at the next integer index) and `[key]` like `$a['key'] = $v`. You can mix them: `contact[]`, `contact[]`, `contact[email]` yields keys `0`, `1` and `email`. ## Multi-selects and checkbox groups A `<select multiple>` sends one pair per selected option, all under the same name. Named `district`, only the last option survives; named `district[]`, `$_POST['district']` is a list of every selection. A group of checkboxes that should allow several answers works the same way: give them all the same `name` ending in `[]` and distinct `value` attributes. If the user selects nothing, the browser sends no pair at all, so the key is **absent** rather than an empty array. ## A worked example: the council survey Picture a city council survey with a resident block, a group of topic checkboxes and a district multi-select: - `resident[name]` and `resident[ward]` - two text inputs grouped under one key; - `topics[]` on five checkboxes with values `parks`, `transit`, `housing`, `libraries`, `safety`; - `district[]` on a `<select multiple>`; - `comment` on a textarea. A resident who fills in a name and ward, ticks parks and transit, picks two districts and leaves the comment empty produces `$_POST['resident']` with two string keys, `$_POST['topics']` as `['parks', 'transit']`, `$_POST['district']` as a two-element list, and `$_POST['comment']` as `""`. A resident who ticks no topic produces **no** `topics` key at all. The handler therefore reads each part with its own default and shape check rather than assuming the full structure is present. ## Reading the result defensively The client chooses the names, so the shape of `$_POST` is not guaranteed. Three situations to handle: 1. **Missing key** - nothing selected, or a field the form did not render. Use `$_POST['topics'] ?? []` rather than reading the key directly, which in PHP 8 raises an "Undefined array key" warning. 2. **An array where you expect a string** - anyone can post `ward[]=4`. In PHP 8, passing that array to `trim()` throws a `TypeError`, because internal functions now reject wrong-typed arguments instead of returning `null`. 3. **A string where you expect an array** - someone posts `topics=parks`. Check with `is_array()` before looping. Validating the actual values (allowed choices, integer ranges) and escaping them when you print them back are separate steps with their own tools. The name-to-key rules above apply equally to `$_GET` for query strings.
- In PHP 8, what happens if someone renames the field ward to ward[] and your code calls trim($_POST['ward'])?`$_POST['ward']` is now an array, and `trim()` declares a `string` parameter, so PHP 8 throws a `TypeError` ("trim(): Argument #1 ($string) must be of type string, array given"). Uncaught, that is a 500 error. Before PHP 8.0 it emitted a warning and returned `null`. Check the shape with `is_string()` or a filter function before calling string functions on request data.
- Does a field named score[03] produce the integer key 3 in $_POST?No. PHP converts a bracket key to an integer only when it is a canonical decimal integer string, the same rule array literals use. `score[3]` gives int key `3`; `score[03]` and `score[3.0]` keep the string keys `"03"` and `"3.0"`.
- Do these naming rules apply to $_GET as well?Yes. A form with `method="get"` puts the same pairs in the query string, and PHP parses them into `$_GET` with the same rules: brackets build arrays, a repeated plain name keeps the last value, and every value is a string.
saying these in an interview costs you the question
- PHP uses the input's id attribute as the $_POST key.
- A number input arrives in $_POST as an int.
- A multi-select named district delivers every selected option as an array.
- Repeating a plain field name makes PHP collect the values into an array.
- Every field rendered in the form always has a key in $_POST.