<?php OOP: Zero to Hero Chapter 16 of 20

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.

๐Ÿ“ฎ
Real-world analogy

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:

GET vs POST โ€” postcards vs sealed envelopes

method="get"method="post"
Where the data travelsIn the URL: save.php?full_name=ChandaHidden inside the request body
AnalogyA postcard โ€” everyone can read itA sealed envelope
Can be bookmarked/shared?YesNo
Use it forโ€ฆSearches, filters, page numbersAnything that changes data: registrations, logins, payments
PHP reads it from$_GET$_POST
Rule of thumb

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";
Received: Chanda Mwila, BSc Computing, K5000

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>";
    }
}
"But the dropdown only has 3 options!"

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: &lt;script&gt;alert('Hacked!')&lt;/script&gt;
// 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);
The two golden security rules โ€” memorise them

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:

โœ๏ธ Try it yourself

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>
Quick quiz

A login form (username + password) should useโ€ฆ

Quick quiz

Which function protects against XSS when printing user data?

Quick quiz

Your form uses a dropdown, so the programme value is always valid โ€” true or false?

๐Ÿ”‘ Key points
  • 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.