Forms are the bridge between browser input and PHP processing. The guide moves from basic form structure to request methods, superglobals, security, empty field handling, dynamic forms, and validation.
1.0 Web Foundations Listed in the Study Guide Outcomes
Scope note: the Topic 1 opening page lists these outcomes (study guide PDF p. 9); the detailed Topic 1 material that follows concentrates on HTML forms and PHP form processing. Treat this as a concise orientation to the stated outcomes, with detail depth limited by the supplied guide.
| Term | Key point |
| Browser and server | The browser requests a resource; the server responds. PHP runs on the server and can produce HTML for the browser. |
| HTTP / HTTPS / FTP | HTTP transfers web requests and responses; HTTPS protects HTTP traffic with TLS; FTP is a file-transfer protocol. |
| URI, URL, DNS | A URI identifies a resource; a URL locates/accesses it; DNS resolves domain names to IP addresses used for routing. |
| Standards and universal design | Standards support consistent behavior across browsers/devices. Universal design aims for usability across different abilities and circumstances; use semantic structure, labels, text alternatives, and keyboard access. |
| Responsive and progressive design | Responsive layouts adapt to screen sizes. Progressive enhancement starts with core accessible content and adds capabilities where available. |
| Ethics | Respect privacy, collect/use personal information responsibly, and keep web content accurate. |
Back to Topics 1 & 2 Test Index ↑
1.1 How HTML Forms Work
A web form is a collection of controls inside a <form> element. Typical controls include text fields, passwords, checkboxes, radio buttons, file selectors, hidden fields, select lists, multi-select boxes, text areas, submit buttons, and reset buttons.
Browser form
Submit button
HTTP request
PHP handler
<form action="form_handler.php" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email">
<button type="submit">Send</button>
</form>
1.2 GET vs POST
The form action says where the data goes. The form method says how it is sent. GET places values in the URL, so use it for non-sensitive searches and bookmarkable requests. POST sends data in the request body and suits submissions that process or change data. POST does not encrypt data; use HTTPS to protect traffic in transit.
| Method |
Where Data Appears |
Best Fit |
Watch Out |
GET |
URL query string |
Small requests, links, searches, bookmarkable pages |
Visible in the address bar and limited by URL length |
POST |
Request body |
Submissions that process or change data; larger request bodies |
POST alone does not encrypt data; use HTTPS for transport protection |
1.3 Capturing Form Data With PHP
PHP uses superglobal arrays to expose request data in any scope. The field name becomes the array key, and the submitted value becomes the array value.
$_GET stores form values sent with method="get".
$_POST stores form values sent with method="post".
$_REQUEST combines GET, POST, and COOKIE values, so it can be ambiguous.
$email = $_POST["emailAddress"];
echo htmlspecialchars($email);
Correct pattern: If you know the form uses POST, read from $_POST instead of $_REQUEST.
1.4 PHP Form Data and Security
The guide warns that submitted values can include malicious HTML or JavaScript. Printing raw input can cause cross-site scripting. Escape output with htmlspecialchars() and validate values before using them.
$safeName = htmlspecialchars($_POST["firstName"]);
if (filter_var($_POST["emailAddress"], FILTER_VALIDATE_EMAIL)) {
echo "Valid email";
} else {
echo "Invalid email";
}
Never do this: Do not display a submitted password back to the page, and do not use raw request values in HTML output.
1.5 Handling Empty Form Fields
Some controls send an empty value when left blank, while others send no key at all. If the key does not exist, reading it directly can produce an undefined index notice.
| Control |
If Empty or Unselected |
PHP Check |
| Text, password, hidden, textarea, file input |
Field name is sent with an empty value |
empty($_POST["field"]) |
| Checkbox, radio, list box, multi-select |
Nothing may be sent |
isset($_POST["field"]) |
| Reset and push buttons |
Nothing is sent |
Do not rely on them as submitted values |
if (isset($_POST["gender"])) {
echo htmlspecialchars($_POST["gender"]);
}
1.6 Dynamic Forms and Validation
The guide describes a PHP-generated form that checks missing required fields, highlights errors, keeps previous values, and shows a thank-you message only after successful validation.
processForm() checks submitted required fields.
displayForm($missingFields) redisplays the form with feedback.
setValue(), setChecked(), and setSelected() preserve user input.
validateField() marks missing fields for styling.
$missing = [];
foreach (["name", "email", "age"] as $field) {
if (empty($_POST[$field])) {
$missing[] = $field;
}
}
Revision task: Practise validating name, email, and age, then extend the same idea to login forms, multi-select lists, and file uploads.
Topic 1 Extended Code Example: Widget Club Form Handler
This example follows the guide's registration.html and process_registration.php idea, but it applies the later security and empty-field rules so the revision version is safer than the first teaching example. It demonstrates form handling only; it does not create an account or store a password.
registration.html: save this form beside process_registration.php. Each control name matches a key used by the handler below.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Widget Club Registration</title>
</head>
<body>
<h1>Join the Widget Club</h1>
<form action="process_registration.php" method="post">
<p>
<label for="firstName">First name</label>
<input id="firstName" name="firstName" type="text" required>
</p>
<p>
<label for="lastName">Last name</label>
<input id="lastName" name="lastName" type="text" required>
</p>
<p>
<label for="password1">Password</label>
<input id="password1" name="password1" type="password" required>
</p>
<p>
<label for="password2">Confirm password</label>
<input id="password2" name="password2" type="password" required>
</p>
<fieldset>
<legend>Gender</legend>
<label><input name="gender" type="radio" value="female" required> Female</label>
<label><input name="gender" type="radio" value="male"> Male</label>
<label><input name="gender" type="radio" value="other"> Prefer to self-describe</label>
</fieldset>
<p>
<label for="favoriteWidget">Favorite widget</label>
<select id="favoriteWidget" name="favoriteWidget">
<option value="">Choose one (optional)</option>
<option value="clockwork bird">Clockwork bird</option>
<option value="pocket telescope">Pocket telescope</option>
<option value="self-stirring mug">Self-stirring mug</option>
</select>
</p>
<p>
<label><input name="newsletter" type="checkbox" value="yes"> Send me the newsletter</label>
</p>
<p>
<label for="comments">Comments (optional)</label>
<textarea id="comments" name="comments" rows="4" cols="40"></textarea>
</p>
<button type="submit">Send registration</button>
</form>
</body>
</html>
process_registration.php: read the submitted values, check required fields, then escape text before displaying it.
<?php
// process_registration.php
// This script receives the Widget Club form through method="post".
// Every key below must match a form control's name attribute.
$requiredFields = ["firstName", "lastName", "password1", "password2", "gender"];
$missingFields = [];
foreach ($requiredFields as $fieldName) {
// empty() catches fields that exist but contain an empty string.
// It is useful for required text and password fields.
if (empty($_POST[$fieldName])) {
$missingFields[] = $fieldName;
}
}
// The newsletter checkbox is optional, so it may not exist in $_POST.
// isset() avoids an undefined index notice when the box was not ticked.
$newsletter = isset($_POST["newsletter"]) ? "yes" : "no";
// Escape output before displaying submitted text in HTML.
$firstName = htmlspecialchars($_POST["firstName"] ?? "");
$lastName = htmlspecialchars($_POST["lastName"] ?? "");
$favoriteWidget = htmlspecialchars($_POST["favoriteWidget"] ?? "");
$comments = htmlspecialchars($_POST["comments"] ?? "");
if ($missingFields) {
echo "<h1>Please complete the required fields</h1>";
echo "<p>Missing: " . htmlspecialchars(implode(", ", $missingFields)) . "</p>";
exit;
}
if ($_POST["password1"] !== $_POST["password2"]) {
echo "<p>The passwords do not match. Please try again.</p>";
exit;
}
echo "<h1>Thank You</h1>";
echo "<dl>";
echo "<dt>First name</dt><dd>$firstName</dd>";
echo "<dt>Last name</dt><dd>$lastName</dd>";
echo "<dt>Gender</dt><dd>" . htmlspecialchars($_POST["gender"]) . "</dd>";
echo "<dt>Favorite widget</dt><dd>$favoriteWidget</dd>";
echo "<dt>Newsletter</dt><dd>$newsletter</dd>";
echo "<dt>Comments</dt><dd>$comments</dd>";
echo "</dl>";
// Exam trap: the guide's first handler printed the submitted password.
// A secure revision answer should never display password values back to the user.
?>
Topic 1 Extended Code Example: Study Session Form With Validation
This single-file example combines an HTML form with a PHP POST handler. It checks that the learner name is present and no longer than 40 characters, that the topic is one of the allowed choices, and that minutes is an integer from 5 to 180. Save it as study-log.php and run it through a PHP-enabled local server. The character-count check uses PHP's mbstring extension.
study-log.php: the handler runs before any HTML is sent, so validation and response output can be decided first.
<?php
// The allowed values are defined by the server, not trusted from the request.
$topics = ["HTML forms", "PHP validation", "Cookies and sessions"];
$form = ["learner" => "", "topic" => "", "minutes" => ""];
$errors = [];
$minutesValue = null;
$submitted = $_SERVER["REQUEST_METHOD"] === "POST";
if ($submitted) {
// Ignore unexpected array input; normal form controls submit strings.
foreach (["learner", "topic", "minutes"] as $field) {
$value = $_POST[$field] ?? "";
$form[$field] = is_string($value) ? trim($value) : "";
}
if ($form["learner"] === "") {
$errors[] = "Your name is required.";
} elseif (mb_strlen($form["learner"], "UTF-8") > 40) {
$errors[] = "Your name must be 40 characters or fewer.";
}
if (!in_array($form["topic"], $topics, true)) {
$errors[] = "Choose a valid study topic.";
}
$minutesValue = filter_var($form["minutes"], FILTER_VALIDATE_INT);
if ($minutesValue === false || $minutesValue < 5 || $minutesValue > 180) {
$errors[] = "Minutes must be a whole number from 5 through 180.";
}
}
function escapeHtml(string $value): string {
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8");
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Study Session Log</title>
</head>
<body>
<h1>Log a study session</h1>
<?php if ($submitted && $errors): ?>
<div role="alert">
<h2>Please correct these items</h2>
<ul>
<?php foreach ($errors as $error): ?>
<li><?= escapeHtml($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php elseif ($submitted): ?>
<p role="status">
<?= escapeHtml($form["learner"]) ?> studied
<?= escapeHtml($form["topic"]) ?> for <?= $minutesValue ?> minutes.
</p>
<?php endif; ?>
<form action="study-log.php" method="post">
<p>
<label for="learner">Your name</label>
<input id="learner" name="learner" type="text" maxlength="40" required
value="<?= escapeHtml($form["learner"]) ?>">
</p>
<p>
<label for="topic">Study topic</label>
<select id="topic" name="topic" required>
<option value="">Choose a topic</option>
<?php foreach ($topics as $topic): ?>
<option value="<?= escapeHtml($topic) ?>"<?= $form["topic"] === $topic ? " selected" : "" ?>><?= escapeHtml($topic) ?></option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="minutes">Minutes (5–180)</label>
<input id="minutes" name="minutes" type="number" min="5" max="180" step="1" required
value="<?= escapeHtml($form["minutes"]) ?>">
</p>
<button type="submit">Save study session</button>
</form>
</body>
</html>
$_SERVER["REQUEST_METHOD"] selects the POST-processing path; the first page visit only displays the form.
$_POST[$field] ?? "" handles absent controls. The is_string() guard also avoids warnings if a request submits an array instead of a normal text value.
in_array(..., true) checks the submitted topic against the server-side allowlist using strict comparison.
filter_var(..., FILTER_VALIDATE_INT) rejects non-integers; the following comparisons enforce the permitted range.
- HTML attributes and displayed text are escaped when output. Browser constraints such as
required, maxlength, and min/max help users but do not replace server-side validation.
Review request superglobals · Review output security · Review form validation
Topic 1 Extended Code Example: Redisplay A Form With Validation
The guide's generated-form section names helpers such as processForm(), displayForm(), setValue(), setChecked(), setSelected(), and validateField(). This compact version shows how those functions fit together.
<?php
// Single-page PHP form: first display, failed redisplay, and success display.
$requiredFields = ["name", "email", "age", "gender"];
function safe($value) {
return htmlspecialchars($value ?? "", ENT_QUOTES);
}
function setValue($fieldName) {
// Keeps typed values when the form is redisplayed after an error.
echo safe($_POST[$fieldName] ?? "");
}
function setChecked($fieldName, $fieldValue) {
// Keeps radio buttons or checkboxes selected after redisplay.
if (isset($_POST[$fieldName]) && $_POST[$fieldName] === $fieldValue) {
echo " checked";
}
}
function validateField($fieldName, $missingFields) {
// Adds an error class to a label when the field is missing.
echo in_array($fieldName, $missingFields, true) ? " class=\"error\"" : "";
}
function processForm($requiredFields) {
$missingFields = [];
foreach ($requiredFields as $fieldName) {
if (empty($_POST[$fieldName])) {
$missingFields[] = $fieldName;
}
}
if (!empty($_POST["email"]) && !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
$missingFields[] = "email format";
}
if (!empty($_POST["age"]) && ($_POST["age"] < 18 || $_POST["age"] > 60)) {
$missingFields[] = "age range";
}
return $missingFields;
}
$missingFields = $_SERVER["REQUEST_METHOD"] === "POST" ? processForm($requiredFields) : [];
if ($_SERVER["REQUEST_METHOD"] === "POST" && !$missingFields) {
echo "<h1>Thanks, " . safe($_POST["name"]) . "</h1>";
echo "<p>Your submitted details passed validation.</p>";
} else {
?>
<form action="<?= safe($_SERVER["PHP_SELF"]) ?>" method="post">
<label<?php validateField("name", $missingFields); ?>>Name</label>
<input type="text" name="name" value="<?php setValue("name"); ?>">
<label<?php validateField("email", $missingFields); ?>>Email</label>
<input type="email" name="email" value="<?php setValue("email"); ?>">
<label<?php validateField("age", $missingFields); ?>>Age</label>
<input type="number" name="age" min="18" max="60" value="<?php setValue("age"); ?>">
<label><input type="radio" name="gender" value="F"<?php setChecked("gender", "F"); ?>> Female</label>
<label><input type="radio" name="gender" value="M"<?php setChecked("gender", "M"); ?>> Male</label>
<button type="submit">Submit</button>
</form>
<?php } ?>
Topic 1 Review Exercise: Process a Multi-Select List
Use name="languages[]" so PHP receives an array. Accept only known options, then escape each value before HTML output.
<?php
$allowed = ["PHP", "JavaScript", "Python"];
$selected = [];
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$raw = $_POST["languages"] ?? [];
if (!is_array($raw)) $raw = [];
$selected = array_values(array_intersect($allowed, $raw));
echo "<p>Selected: " . htmlspecialchars(implode(", ", $selected), ENT_QUOTES, "UTF-8") . "</p>";
}
?>
<form method="post">
<label for="languages">Favourite languages</label>
<select id="languages" name="languages[]" multiple size="3">
<?php foreach ($allowed as $language): ?>
<option value="<?= htmlspecialchars($language, ENT_QUOTES, "UTF-8") ?>"<?= in_array($language, $selected, true) ? " selected" : "" ?>><?= htmlspecialchars($language, ENT_QUOTES, "UTF-8") ?></option>
<?php endforeach; ?>
</select>
<button type="submit">Submit</button>
</form>
Topic 1 Review Exercise: Validate and Display an Image Upload
This matches the review requirements: JPG/JPEG/PNG only, at most 2 MB, saved in uploads/, with an error if a check fails. The image content is checked as well as the filename extension. Save as upload.php and ensure the web server can write to the project folder.
<?php
$message = "";
$imageUrl = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$file = $_FILES["photo"] ?? null;
if (!$file || $file["error"] !== UPLOAD_ERR_OK) {
$message = "Choose an image and try again.";
} elseif ($file["size"] > 2 * 1024 * 1024) {
$message = "The image must be 2 MB or smaller.";
} else {
$extension = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION));
$type = (new finfo(FILEINFO_MIME_TYPE))->file($file["tmp_name"]);
$allowed = ["jpg" => "image/jpeg", "jpeg" => "image/jpeg", "png" => "image/png"];
if (!isset($allowed[$extension]) || $allowed[$extension] !== $type || getimagesize($file["tmp_name"]) === false) {
$message = "Only JPG, JPEG, and PNG images are allowed.";
} else {
$folder = __DIR__ . "/uploads";
if (!is_dir($folder) && !mkdir($folder, 0755)) {
$message = "Could not create the uploads folder.";
} else {
$name = bin2hex(random_bytes(16)) . "." . $extension;
if (move_uploaded_file($file["tmp_name"], $folder . "/" . $name)) {
$imageUrl = "uploads/" . $name;
} else {
$message = "Could not save the uploaded image.";
}
}
}
}
}
?>
<form method="post" enctype="multipart/form-data">
<label for="photo">Image (JPG or PNG, up to 2 MB)</label>
<input id="photo" name="photo" type="file" accept=".jpg,.jpeg,.png" required>
<button type="submit">Upload</button>
</form>
<?php if ($message !== ""): ?><p><?= htmlspecialchars($message, ENT_QUOTES, "UTF-8") ?></p><?php endif; ?>
<?php if ($imageUrl !== ""): ?><img src="<?= htmlspecialchars($imageUrl, ENT_QUOTES, "UTF-8") ?>" alt="Uploaded image"><?php endif; ?>
Topic 1 Review Exercise: Password Hash and Prepared Login Query
The review requires a prepared statement, password hashing, and safe output. Assume a users table with username and password_hash columns, a working $conn MySQLi connection, and HTTPS. Save the hash at registration; never save the original password.
<?php
// During registration, after validating the submitted password:
$hash = password_hash($password, PASSWORD_DEFAULT);
$insert = mysqli_prepare($conn, "INSERT INTO users (username, password_hash) VALUES (?, ?)");
mysqli_stmt_bind_param($insert, "ss", $username, $hash);
mysqli_stmt_execute($insert);
mysqli_stmt_close($insert);
// During login, retrieve the stored hash with a parameterized query:
$username = trim($_POST["username"] ?? "");
$password = $_POST["password"] ?? "";
$query = mysqli_prepare($conn, "SELECT password_hash FROM users WHERE username = ?");
mysqli_stmt_bind_param($query, "s", $username);
mysqli_stmt_execute($query);
mysqli_stmt_bind_result($query, $storedHash);
$found = mysqli_stmt_fetch($query);
mysqli_stmt_close($query);
if ($found && password_verify($password, $storedHash)) {
session_start();
session_regenerate_id(true);
$_SESSION["username"] = $username;
echo "Welcome, " . htmlspecialchars($username, ENT_QUOTES, "UTF-8");
} else {
echo "Invalid username or password.";
}
Check connection and statement errors in a real application. The form uses method="post" and password input type="password"; output escaping protects the greeting, while the prepared query protects SQL parameters.
Topic 1 Glossary: Forms, Request Data And Validation
<form>
- The HTML element that groups form controls and defines how submitted data is sent.
action
- The form attribute that names the page or script that receives the submitted data.
method
- The form attribute that chooses the HTTP submission method, usually
get or post.
GET
- A request method that sends field names and values in the URL query string.
POST
- A request method that sends form data in the request body and supports larger submissions.
name
- The form-control attribute PHP uses as the key in
$_GET or $_POST.
value
- The submitted value paired with a form control's
name.
$_GET
- The PHP superglobal array containing values submitted with the GET method.
$_POST
- The PHP superglobal array containing values submitted with the POST method.
$_REQUEST
- A combined request array that can include
$_GET, $_POST, and $_COOKIE; useful but less precise.
superglobal
- A built-in PHP variable available in any scope, including inside functions.
htmlspecialchars()
- Converts special HTML characters to entities so submitted text is safer to display.
filter_var()
- A PHP validation/filtering function used in the guide with
FILTER_VALIDATE_EMAIL.
isset()
- Checks whether a submitted key exists before the script reads it.
empty()
- Checks whether a submitted value is absent, blank, or otherwise empty.
array_key_exists()
- Alternative key-existence check mentioned for avoiding undefined index notices.
Undefined index
- A PHP notice caused by trying to read a field that was not submitted.
XSS
- Cross-site scripting; a risk when user-submitted HTML or JavaScript is displayed as executable page content.
setValue()
- A helper pattern that repopulates a text field after validation fails.
setChecked()
- A helper pattern that keeps a checkbox or radio option selected after form redisplay.
setSelected()
- A helper pattern that keeps the correct
<option> selected in a dropdown.
validateField()
- A helper pattern that marks missing fields with an error style.
password_hash()
- Review-question function for storing passwords as hashes instead of plain text.
password_verify()
- Review-question function for checking a typed password against a stored hash.
prepared statements
- Review-question security technique for reducing SQL injection risk in login code.
$_FILES
- The PHP superglobal used for uploaded file information in the file-upload exercise.
move_uploaded_file()
- The PHP function used to move an uploaded file into a chosen folder such as
uploads/.