Handling a Form

A form posts its fields to a PHP script; each input's name attribute is the array key your code reads.

Syntax$value = trim($_POST['field_name'] ?? '');

A form is how a visitor sends data to your script. The <form> tag has two attributes that matter: action, the URL the data goes to, and method, either get or post.

GET or POST

With get the fields are appended to the URL as a query string and arrive in $_GET. The URL can be bookmarked, shared and reloaded safely, which suits a search box or a filter. With post the fields travel in the request body and arrive in $_POST. Nothing appears in the URL and there is no practical length limit, which suits anything that changes data.

The name attribute is the contract

Each input's name becomes the array key. <input name="email"> arrives as $_POST['email']. Rename the input and the script stops seeing the field, so the markup and the code have to change together. A name written as topics[] collects several values into an array instead.

Reading the fields

Read every field with ?? so a missing key is not a warning: trim($_POST['email'] ?? ''). Values always arrive as strings, even numbers. One file can both show the form and handle the submission, by checking $_SERVER['REQUEST_METHOD'].

What trips people up

$_POST is filled only for a POST whose body is form-encoded. For a PUT or PATCH body, call request_parse_body(), added in PHP 8.4.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Accept a contact message and keep what the visitor typed when a field is rejected.
  • Build a filter form whose URL can be bookmarked and shared.
  • Collect several checkbox answers into one array with a topics[] field name.
  • Handle a PUT body from a client that is not a browser form.

More examples

One file that shows the form and handles it

The PHP block runs first and decides what the markup below should show, which is how a rejected form keeps what the visitor typed. Two shorthands appear here. <?= $x ?> is shorthand for <?php echo $x; ?> and is always available. The if/endif form with a colon is the same if statement as the one with braces, written so the HTML in between stays readable. Both forms mean exactly the same thing, and you will meet each of them in other people's templates.

Example · php
<?php

declare(strict_types=1);

$errors = [];
$name = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = trim($_POST['name'] ?? '');

    if ($name === '') {
        $errors['name'] = 'Name is required.';
    }

    if ($errors === []) {
        // store the message here, then send the browser somewhere else
        header('Location: /thanks', true, 303);
        exit;
    }
}
?>
<form method="post" action="/contact">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" value="<?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>">
    <?php if (isset($errors['name'])): ?>
        <p><?php echo htmlspecialchars($errors['name'], ENT_QUOTES, 'UTF-8'); ?></p>
    <?php endif; ?>
    <button type="submit">Send</button>
</form>

GET for a search that stays linkable

A search is a good fit for GET, because the result can be bookmarked and a refresh is harmless. Building the next-page link by hand risks forgetting to encode a term containing & or a space, so http_build_query() does the encoding from an array. The finished URL is then escaped again for the attribute, because it is going into HTML.

Example · php
<?php

declare(strict_types=1);

$term = trim($_GET['q'] ?? '');
$page = max(1, (int) ($_GET['page'] ?? 1));

$next = '/search?' . http_build_query(['q' => $term, 'page' => $page + 1]);

echo '<p>Searching for ' . htmlspecialchars($term, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<a href="' . htmlspecialchars($next, ENT_QUOTES, 'UTF-8') . '">Next page</a>';

A body that PHP does not fill in for you

Browsers only send get and post from a form, but code calling your URLs can use PUT or PATCH. Before PHP 8.4 that body had to be read and decoded by hand. request_parse_body() returns a pair: the fields, shaped like $_POST, and the files, shaped like $_FILES. It throws RequestParseBodyException when the body is not a form encoding it understands.

Example · php
<?php

declare(strict_types=1);

// $_POST is populated only for a POST request with a form content type.
// request_parse_body() (PHP 8.4 and later) parses a form body on any method.
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
    try {
        [$fields, $files] = request_parse_body();
    } catch (RequestParseBodyException $e) {
        http_response_code(415);
        exit('Unsupported body.');
    }

    $title = trim($fields['title'] ?? '');
    echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
}

Discussion

  • Be the first to comment on this lesson.