PHP Form Handling Tutorial for Beginners (GET vs POST)
New to PHP forms? This beginner tutorial explains PHP form handling: how $_GET and $_POST work, safe input validation, and a full working contact form example.
New to PHP forms? This beginner tutorial explains PHP form handling: how $_GET and $_POST work, safe input validation, and a full working contact form example.
If you searched for "PHP form handling for beginners," here is the short answer: PHP reads data from an HTML form using two built-in arrays called $_GET and $_POST. You add a <form> tag to your HTML page, PHP reads whatever the visitor typed, and then you can check it, clean it, and use it (for example, save it to a database). This guide walks through both methods step by step, with small code examples you can copy and run.
A web form is just HTML: text boxes, checkboxes, and a submit button. On its own, HTML cannot save that data anywhere or do anything with it — it can only display the boxes. PHP is a server-side language, which means it runs on the web server, not in the visitor's browser. When someone submits a form, the browser sends the typed data to your server, and PHP is the code that receives it, reads it, and decides what happens next (show a message, save it to a database, send an email, and so on).
This post assumes you already have a basic PHP file running (for example, from our guide on connecting PHP to a database, linked below). If you are completely new to PHP syntax, skim that post first, then come back here.
Every form needs two things: a method (how the data travels) and an action (which file receives it). Here is a minimal contact form:
<form method="post" action="process.php">
<label for="name">Your name:</label>
<input type="text" id="name" name="name">
<label for="email">Your email:</label>
<input type="email" id="email" name="email">
<button type="submit">Send</button>
</form>Line by line: method="post" tells the browser to send the data using the POST method (explained below). action="process.php" tells the browser which PHP file should receive the data when the form is submitted. Each input has a name attribute (name="name", name="email") — PHP uses these exact names as keys to read the values, so they must be spelled correctly. The button with type="submit" triggers the form to send.
$_GET is a special PHP array that always exists and holds any data sent using the GET method. With GET, the form data is attached to the end of the URL, like this: process.php?name=Alex&email=alex@example.com. That makes GET useful for things like search boxes or filters, because the result page can be bookmarked or shared as a link. It is not suitable for passwords or sensitive data, since anyone can see it in the URL.
<?php
// process.php - reached from a form with method="get"
$name = $_GET['name'];
echo "Hello, " . $name . "!";
?>Line by line: $_GET['name'] looks up the value that was sent under the key name (matching the input's name="name" attribute). We store it in a normal PHP variable called $name. The echo line prints a greeting, and the dot (.) joins, or "concatenates," two pieces of text together.
$_POST works the same way as $_GET, but it reads data sent using the POST method. With POST, the data travels inside the request body instead of the URL, so it is not visible in the address bar and there is no practical size limit. This is why POST is the standard choice for login forms, sign-up forms, and anything that changes data (like saving a new record).
<?php
// process.php - reached from a form with method="post"
$name = $_POST['name'];
$email = $_POST['email'];
echo "Thanks, " . $name . ". We'll reply at " . $email . ".";
?>This is identical in structure to the $_GET example — only the array name changes. That is the whole trick: match the method in your <form> tag (get or post) to the superglobal array you read from ($_GET or $_POST).
| GET | POST | |
|---|---|---|
| Where data appears | In the URL | Hidden in the request body |
| Good for | Searches, filters, pagination links | Logins, sign-ups, saving data |
| Can be bookmarked/shared | Yes | No |
| Practical data size limit | Small (URL length limits) | Large |
| Safe for passwords | No | Yes, when combined with HTTPS |
Two problems come up as soon as your form goes live. First, if someone opens process.php directly (without submitting the form), $_POST['name'] will not exist, and PHP will show a warning. Second, if a visitor types HTML or a <script> tag into your form, printing it back out unprotected can break your page or create a security hole called XSS (cross-site scripting). Here is a safer version:
<?php
if (isset($_POST['name']) && isset($_POST['email'])) {
$name = htmlspecialchars($_POST['name']);
$email = htmlspecialchars($_POST['email']);
echo "Thanks, " . $name . ". We'll reply at " . $email . ".";
} else {
echo "Please fill out the form first.";
}
?>isset() checks whether a variable exists and is not null, before you try to use it — this stops the "undefined array key" warning. The && means "and," so both fields must be set for the first branch to run. htmlspecialchars() converts special characters like < and > into safe text before they are shown on the page, which is a simple, essential defense against XSS. The else branch runs if the form has not been submitted yet, so the page does not break when visited directly.
Checking that a field is set is not the same as checking it has a useful value — a visitor could submit an empty text box. PHP's trim() removes extra spaces, empty() checks for blank values, and filter_var() with FILTER_VALIDATE_EMAIL checks that an email address is correctly formatted.
<?php
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$errors = [];
if (empty($name)) {
$errors[] = "Please enter your name.";
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Please enter a valid email address.";
}
if (empty($errors)) {
echo "Form looks good!";
} else {
foreach ($errors as $error) {
echo $error . "<br>";
}
}
?>The ?? '' is the "null coalescing" operator: it uses '' (an empty string) as a fallback if $_POST['name'] is not set, so the script never crashes. $errors is a plain PHP array we use to collect problem messages. filter_var($email, FILTER_VALIDATE_EMAIL) returns false when the email is badly formatted, so the ! (not) flips that into "if this is NOT a valid email." The foreach loop walks through every message in $errors and prints each one.
Here is the full, working pair of files. Save the first as index.html and the second as process.php in the same folder, then open index.html in a browser served by PHP (for example, run php -S localhost:8000 in that folder and visit http://localhost:8000).
<!-- index.html -->
<form method="post" action="process.php">
<input type="text" name="name" placeholder="Your name">
<input type="email" name="email" placeholder="Your email">
<button type="submit">Send</button>
</form><?php
// process.php
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$errors = [];
if (empty($name)) {
$errors[] = "Please enter your name.";
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Please enter a valid email address.";
}
if (empty($errors)) {
$safeName = htmlspecialchars($name);
echo "Thanks, " . $safeName . "! Your message was received.";
// Next step: save $name and $email to a database.
} else {
foreach ($errors as $error) {
echo htmlspecialchars($error) . "<br>";
}
}
?>This combines every piece from the guide: the form posts to process.php, the PHP file trims and validates the input, collects any problems in $errors, and only shows the "thanks" message once the data is valid. The comment marks exactly where you would add database code next.
$_GET reads data sent in the URL (like process.php?name=Alex), while $_POST reads data sent hidden in the request body. Use $_GET for things like search or filter links you want to share, and $_POST for logins, sign-ups, and anything that changes data.
Set method="post" or method="get" on your HTML <form> tag, then in the PHP file named in the form's action, read $_POST['fieldname'] or $_GET['fieldname'], where fieldname matches the input's name attribute exactly.
This happens when the PHP file tries to read $_POST['name'] before the form has actually been submitted, so the key does not exist yet. Wrap the read in an isset($_POST['name']) check, or use $_POST['name'] ?? '' to supply a default value.
$_REQUEST contains a merge of GET, POST, and cookie data, so it is less predictable — you cannot tell where a value came from. Most tutorials, including this one, recommend using $_GET or $_POST specifically so your code is clear about which method it expects.
Always pass user-supplied text through htmlspecialchars() before printing it back into HTML. This converts characters like < and > into safe entities so a visitor cannot inject a working <script> tag through your form.
Ready to go further and store form submissions permanently? Learn how to connect your PHP script to a real database.
Read: How to Connect PHP to a MySQL DatabaseBuilding a real project and want a hand? Our team can help you turn a simple form into a full application.
Talk to FepiqOccasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.
Keep reading
What is an index in SQL? A plain-English guide with copy-paste examples showing how indexes speed up queries and when you actually need one.
Learn JavaScript DOM manipulation for beginners: select elements, change text and styles, and handle clicks with simple, copy-paste code examples.
Let's build something
Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.