18. PHP Forms
What you will learn
- How a form sends data to PHP
- How to handle a form in the same file
- How to read text, choices, and checkboxes
- Why you redirect after a successful POST
How it works
- The visitor fills in an HTML form and presses submit.
- The browser sends the values to the address in
action, using themethod. - PHP receives them in
$_POST(or$_GET). - Your code checks them, does the work, and shows a result.
Each field’s name attribute becomes the key in the array. A field without a name is never sent.
A first form
An empty action (or leaving it out) sends the form back to the same file. This is the simplest set-up for learning.
PHP code: hello-form.php
<?php
$message = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$name = trim($_POST["name"] ?? "");
$message = "Hello, " . htmlspecialchars($name, ENT_QUOTES, "UTF-8") . "!";
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello form</title>
</head>
<body>
<form method="post">
<label for="name">Your name</label>
<input type="text" id="name" name="name">
<button type="submit">Say hello</button>
</form>
<p><?php echo $message; ?></p>
</body>
</html>
What you see (a display-only demo)
After typing Sam and submitting, the paragraph below the form shows: Hello, Sam!
Step by step
- The first time the page loads, the method is
GET, so theifblock is skipped and the message is empty. - When the form is submitted, the method is
POST, so PHP readsname, makes it safe, and builds the message. - The page is then drawn, with the message under the form.
Different kinds of field
| Field | How it arrives in $_POST |
|---|---|
| Text, email, textarea | A string |
| Number | A string (convert it) |
| Radio buttons | The value of the chosen one. Missing if none is chosen |
| Single checkbox | Its value if ticked. Missing if not ticked |
Several checkboxes (name="tags[]") |
An array of the ticked values |
| Select drop-down | The chosen value |
PHP code: survey.php (the processing part)
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$level = $_POST["level"] ?? ""; // radio
$topics = $_POST["topics"] ?? []; // checkboxes, an array
$news = isset($_POST["newsletter"]); // single checkbox
echo "Level: " . htmlspecialchars($level, ENT_QUOTES, "UTF-8") . "<br>";
echo "Topics: " . htmlspecialchars(implode(", ", $topics), ENT_QUOTES, "UTF-8") . "<br>";
echo "Newsletter: " . ($news ? "yes" : "no");
}
HTML code: survey.php (the form part)
<form method="post">
<fieldset>
<legend>Your level</legend>
<input type="radio" id="l1" name="level" value="beginner">
<label for="l1">Beginner</label>
<input type="radio" id="l2" name="level" value="advanced">
<label for="l2">Advanced</label>
</fieldset>
<fieldset>
<legend>Topics</legend>
<input type="checkbox" id="t1" name="topics[]" value="html">
<label for="t1">HTML</label>
<input type="checkbox" id="t2" name="topics[]" value="php">
<label for="t2">PHP</label>
</fieldset>
<input type="checkbox" id="n" name="newsletter" value="yes">
<label for="n">Send me the newsletter</label>
<button type="submit">Send</button>
</form>
Output after choosing Beginner, HTML and PHP, and ticking the newsletter
Level: beginner
Topics: html, php
Newsletter: yes
The square brackets in name="topics[]" tell PHP to collect every ticked box into an array.
Redirect after POST
If a visitor refreshes a page that was just reached by a POST, the browser offers to send the form again, which could create a second order or message. The fix is the Post / Redirect / Get pattern: after a successful POST, send the browser to a normal page with header().
PHP code
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// ...validate and save the data here...
header("Location: thanks.php");
exit;
}
header()must run before any output, including blank lines before<?php. Otherwise you get “headers already sent”.- Always call
exitstraight after, or the rest of the script keeps running.
Fake requests: why a token matters
A malicious site can trick a logged-in visitor’s browser into sending a form to your site. This is called CSRF. The defence is a secret, random token stored in the session and placed in the form as a hidden field, which PHP checks on submit. You will build one in the Security lesson. WordPress has its own version, called a nonce, which you saw in the plugin code for this site.
Sending email from a form
A contact form often emails you. Putting visitor text straight into email headers allows header injection, where an attacker adds extra recipients. In real projects, use a well-tested mail library or a service, and validate carefully. On WordPress, use wp_mail() and a form plugin that handles this for you.
Local sites cannot usually send real email. LocalWP includes a tool to catch outgoing mail for testing, so use that rather than expecting delivery.
Common mistakes
- Forgetting
method="post", so the form sends GET and nothing is in$_POST. - Fields with no
name, so nothing arrives. - Reading
$_POSTwithout checking the keys exist. - Treating a missing checkbox as “no” without testing
isset(). - Printing the values without
htmlspecialchars(). - Not redirecting after a successful POST.
- Output before
header().
Exercise
Build a form with one text field called city. When it is submitted, print You live in and the city, safely. If the field is empty, print Please enter a city.
Show solution
<?php
$message = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$city = trim($_POST["city"] ?? "");
if ($city === "") {
$message = "Please enter a city.";
} else {
$message = "You live in " . htmlspecialchars($city, ENT_QUOTES, "UTF-8");
}
}
?>
<form method="post">
<label for="city">City</label>
<input type="text" id="city" name="city">
<button type="submit">Send</button>
</form>
<p><?php echo $message; ?></p>
Knowledge check
1. Which attribute becomes the key in $_POST?
The field’s name.
2. How do you collect several ticked checkboxes into an array?
Give them the same name ending in [], such as topics[].
3. Why redirect after a successful POST?
So refreshing the page does not send the form a second time.
Summary
- Fields arrive in
$_POSTby theirname, and a form can post to its own file. - Unticked checkboxes are missing, and grouped checkboxes arrive as an array.
- Escape output, redirect after success, and protect real forms against CSRF.