A form sends data to the server; PHP reads it out of $_POST or $_GET. The reading part takes one line. Everything that matters is what you do next.
<?php
$name = $_POST["name"] ?? "";
?>The form#
<form method="post" action="signup.php">
<label for="name">Name</label>
<input id="name" name="name" type="text" required>
<label for="email">Email</label>
<input id="email" name="email" type="email" required>
<button type="submit">Sign up</button>
</form>The name attribute is what PHP uses as the key. No name, no data — a field with only an id never arrives.
GET or POST?#
| GET | POST | |
|---|---|---|
| Data goes in | The URL | The request body |
| Bookmarkable | Yes | No |
| Use for | Searches, filters | Anything that changes data |
Rule of thumb: if submitting it twice would be a problem, use POST. Passwords and personal details must never travel in a URL, because URLs end up in browser history and server logs.
Reading the data safely#
<?php
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
// Someone loaded the page normally — show the empty form.
$name = $email = "";
$errors = [];
} else {
$name = trim($_POST["name"] ?? "");
$email = trim($_POST["email"] ?? "");
$errors = [];
}
?>The ?? "" matters. Reading a key that was not submitted raises a warning and gives you null, and a missing field is completely normal — anyone can send a request without it.
Validating#
<?php
if ($name === "") {
$errors["name"] = "Please enter your name.";
} elseif (mb_strlen($name) > 100) {
$errors["name"] = "That name is too long.";
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors["email"] = "That does not look like an email address.";
}
if (!$errors) {
// safe to use
}
?>filter_var with FILTER_VALIDATE_EMAIL saves you writing a regular expression that will be wrong at the edges.
Escaping output#
When you print submitted data back — in an error message, a confirmation, a comment — escape it, or a submitted <script> tag will run in the next visitor’s browser.
<?php
function e(string $value): string {
return htmlspecialchars($value, ENT_QUOTES, "UTF-8");
}
?>
<input name="name" value="<?= e($name) ?>">
<p>Thanks, <?= e($name) ?></p>A short helper like e() makes it hard to forget. Escaping is about output, not input — store what the user typed, escape it when you display it.
Keeping the values on error#
Nothing is more annoying than a form that clears itself because one field was wrong. Print the submitted value back into each input:
<div class="field">
<label for="email">Email</label>
<input id="email" name="email" type="email" value="<?= e($email) ?>">
<?php if (isset($errors["email"])): ?>
<p class="error"><?= e($errors["email"]) ?></p>
<?php endif; ?>
</div>Post-redirect-get#
After a successful POST, redirect instead of printing the result directly. Otherwise refreshing the page resubmits the form, and people do refresh.
<?php
if (!$errors) {
saveSignup($name, $email);
header("Location: thanks.php");
exit; // always exit after a redirect
}
?>The exit is not optional — without it PHP carries on executing the rest of the script.
File uploads#
<form method="post" enctype="multipart/form-data">
<input type="file" name="photo">
</form><?php
if ($_FILES["photo"]["error"] === UPLOAD_ERR_OK) {
$tmp = $_FILES["photo"]["tmp_name"];
// Check the real type, never the submitted filename.
$type = mime_content_type($tmp);
$allowed = ["image/jpeg" => "jpg", "image/png" => "png"];
if (isset($allowed[$type])) {
$name = bin2hex(random_bytes(8)) . "." . $allowed[$type];
move_uploaded_file($tmp, __DIR__ . "/uploads/" . $name);
}
}
?>Two rules: never use the uploaded filename as-is, and never decide the file type from its extension. Both are how attackers get a script into an uploads folder.
Questions people ask#
What is a CSRF token?
A random value stored in the session and included as a hidden field, checked on submit. It stops another site submitting your form on a logged-in user’s behalf. Any form that changes data should have one.
Should I use filter_input instead of $_POST?
filter_input(INPUT_POST, "email", FILTER_VALIDATE_EMAIL) reads and validates in one step, which is tidy. Reading with ?? and validating separately is easier to follow while learning.
Why is my form data empty?
Check the name attributes exist, the method matches what you are reading, and the action points at the right file. Print var_dump($_POST) at the top to see exactly what arrived.
Do I still need server validation with JavaScript validation?
Yes, always. JavaScript validation is for the user’s convenience and can be bypassed entirely.