Chapter 16 ยท Databases & Forms
HTML Forms & Processing
Until now, all data came from us, the programmers. Real applications get data from users โ through HTML forms. This chapter covers building forms, receiving their data in PHP, validating it, and displaying it safely.
A form is a paper application form plus a postbox. The
<input> fields are the blanks to fill, the submit button drops the envelope
in the box, and the form's action is the address on the envelope โ which PHP
file should receive and open it.
A basic form
register.php<form action="save.php" method="post">
<label>Full name:
<input type="text" name="full_name">
</label>
<label>Programme:
<select name="program">
<option value="BSc Computing">BSc Computing</option>
<option value="LLB">LLB</option>
<option value="BBA">BBA</option>
</select>
</label>
<label>Fee balance:
<input type="number" name="fee_balance" step="0.01">
</label>
<button type="submit">Register student</button>
</form>
The two attributes that matter most:
action="save.php"โ the file that receives the envelope.name="full_name"on each field โ the label on each blank. No name, no data: PHP identifies every value by its field's name. This is the most commonly forgotten attribute in beginner code!
GET vs POST โ postcards vs sealed envelopes
method="get" | method="post" | |
|---|---|---|
| Where the data travels | In the URL: save.php?full_name=Chanda | Hidden inside the request body |
| Analogy | A postcard โ everyone can read it | A sealed envelope |
| Can be bookmarked/shared? | Yes | No |
| Use it forโฆ | Searches, filters, page numbers | Anything that changes data: registrations, logins, payments |
| PHP reads it from | $_GET | $_POST |
Asking a question โ GET. Changing something โ POST. Passwords โ POST, always (imagine a password sitting in the browser history as a URL!).
Receiving the data
$_POST is a superglobal โ an associative array PHP fills automatically, keyed by
the fields' name attributes:
save.php<?php
// ?? "" means: if the field is missing, use "" instead of an error (Chapter 12!)
$name = trim($_POST["full_name"] ?? "");
$program = trim($_POST["program"] ?? "");
$balance = $_POST["fee_balance"] ?? 0;
echo "Received: $name, $program, K$balance";
Never trust the envelope: validation
Users make mistakes; attackers make attacks. Both arrive through the same postbox. Every value must be checked before use. The professional pattern collects all problems into an array, so the user sees everything at once:
$errors = [];
if ($name === "") {
$errors[] = "Full name is required.";
} elseif (strlen($name) < 3) {
$errors[] = "Full name must be at least 3 characters.";
}
$allowedPrograms = ["BSc Computing", "LLB", "BBA"];
if (!in_array($program, $allowedPrograms)) {
$errors[] = "Please choose a valid programme.";
}
if (!is_numeric($balance) || $balance < 0) {
$errors[] = "Fee balance must be a positive number.";
}
if (count($errors) === 0) {
echo "All good โ save to the database!";
} else {
foreach ($errors as $e) {
echo "<p style='color:red'>$e</p>";
}
}
So why check the programme against a list? Because forms can be faked.
Anyone can save your form's HTML, edit the options, or send hand-crafted requests with tools.
Browser-side restrictions (dropdowns, required, type="number") are
a convenience for honest users โ PHP-side validation is the actual security.
Displaying user data safely โ XSS
SQL injection's evil twin: what if a user types this as their "name"?
<script>alert('Hacked!')</script>
If you later echo that name into a page, the browser runs it as JavaScript โ on the screens of every visitor. That's XSS (cross-site scripting). The cure is one function, used every single time user data is printed into HTML:
echo htmlspecialchars($name);
// Prints the harmless text: <script>alert('Hacked!')</script>
// The browser DISPLAYS it instead of RUNNING it.
Many developers define a tiny shortcut and use it everywhere โ we will do this in the project:
function e(?string $value): string {
return htmlspecialchars($value ?? "", ENT_QUOTES, "UTF-8");
}
echo "Welcome, " . e($name);
1. User data going into SQL โ prepared statement blanks (Chapter 15).
2. User data going onto the screen โ htmlspecialchars().
Follow both forever and you've dodged the two most common attacks on the web.
The self-processing sticky form
The most useful real-world pattern: one file shows the form and processes it. If validation fails, the form reappears with the user's typing preserved ("sticky") and errors shown. Read it slowly โ this is the skeleton of the project's forms:
register.php<?php
function e(?string $v): string { return htmlspecialchars($v ?? "", ENT_QUOTES, "UTF-8"); }
$name = "";
$errors = [];
$success = false;
// Was the form submitted? (Otherwise this is the first visit โ just show the form)
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$name = trim($_POST["full_name"] ?? "");
if ($name === "") $errors[] = "Name is required.";
elseif (strlen($name) < 3) $errors[] = "Name must be at least 3 characters.";
if (count($errors) === 0) {
// (next chapter: save to database here)
$success = true;
}
}
?>
<!DOCTYPE html>
<html>
<body>
<h1>Register a student</h1>
<?php if ($success): ?>
<p style="color:green">โ Registered <?= e($name) ?> successfully!</p>
<?php endif; ?>
<?php foreach ($errors as $error): ?>
<p style="color:red"><?= e($error) ?></p>
<?php endforeach; ?>
<form method="post"> <!-- no action = submit to myself -->
<label>Full name:
<input type="text" name="full_name" value="<?= e($name) ?>">
</label>
<button type="submit">Register</button>
</form>
</body>
</html>
New tricks in there:
$_SERVER["REQUEST_METHOD"]โ "GET" on a normal visit, "POST" after submitting.<?= ... ?>โ shorthand for<?php echo ... ?>.value="<?= e($name) ?>"โ refills the box with what the user typed: the "sticky" part.if (...): ... endif;andforeach (...): ... endforeach;โ the template-friendly way to mix PHP and HTML without a jungle of curly braces.
Extend the sticky form with an email field. Validate it with
filter_var($email, FILTER_VALIDATE_EMAIL) โ a built-in that returns false for
invalid emails โ and keep it sticky too.
Show the key additions
// In the PHP block:
$email = trim($_POST["email"] ?? "");
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Please enter a valid email address.";
}
// In the form:
<label>Email:
<input type="text" name="email" value="<?= e($email) ?>">
</label>
A login form (username + password) should useโฆ
Which function protects against XSS when printing user data?
Your form uses a dropdown, so the programme value is always valid โ true or false?
- Every input needs a
nameโ PHP identifies data by it, via$_POST/$_GET. - GET = postcard (searches, filters). POST = sealed envelope (changes, logins).
- Validate everything on the server; collect errors in an array and show them together.
- Print user data only through
htmlspecialchars()โ the XSS shield. - The sticky self-processing form: check
REQUEST_METHOD, validate, re-show the form with values preserved. <?= ?>,if: endif;,foreach: endforeach;keep PHP-in-HTML readable.