Forms are the main way for your visitors to input information on your site. They make interactivity possible.
For instance, on a forum, you type some text and click a button to send your message. Same thing with a guestbook or a mini-chat. Basically, we need forms everywhere to exchange data with our users.
You'll notice a lot of HTML coming back in this chapter... and that's no accident: PHP and HTML go hand in hand here. HTML is used to build the form, and PHP is used to handle the data the visitor submits.
This chapter is especially important — we'll be reusing what you learn here throughout the rest of the course. So stay focused!
Creating the Form's Foundation
In HTML, we use the <form>
tag to create a form. It looks like this:
<form method="post" action="destination.php"><p> This is where we'll insert the form elements. </p>
</form>
Everything that makes up our form will go between the opening and closing <form>
tags. If you've already read my HTML/CSS course, a good chunk of this chapter will feel familiar, since I'll go over how to insert the form fields into your page. But here's the new part: we're also going to see how to process the data using PHP.
Let me draw your attention to the very first line of the code. There are two crucial attributes to know for the <form>
tag: method and action. You absolutely need to understand what they do.
The Method
There are several ways to send form data — different "methods." The two main ones are:
- get: the data is sent through the URL, as we've seen before. You retrieve it using the
$_GET
array. This method is rarely used because you can't send much data via the URL (as I mentioned earlier, it's best not to exceed 256 characters). - post: the data is not passed through the URL, so the user won't see it in the address bar. You can send as much data as you want, which is why it's the most common method. However, the data isn't any more secure than with GET — you should always check that all the required values are present and valid, just like we did before. Don't blindly trust forms any more than URLs.
It's up to you to choose the method for sending your form data. If you're unsure, know this: in 99% of cases, we use post, so you'll most often write method="post"
like I did above.
The Action
The action attribute defines which page the form will send data to. That's the page that will receive and process the form's data.
Let's imagine the following setup:
form.php → destination.php
Here, the form is in form.php
. This page doesn't do any processing, but once the form is submitted (by clicking the "Submit" button), the user is redirected to destination.php
, which receives the form data.
The target page's name is set via the action attribute.
destination.php
— I'm using that name to make it clear that it's the page being called. In fact, nothing stops the form from calling itself. You could simply write action="form.php"
. In that case, the same page must be able to both display the form and process the data. That's totally doable, but to avoid making things too complex right now, we'll steer clear of that. ;)So, remember this: you'll usually be working with two different pages — the one that contains the form (form.php
in our example), and the one that handles the data (destination.php
).
Form Elements
As you may already know, forms can contain all kinds of elements: text fields, buttons, checkboxes, and more.
I'll go through each one and show you how to use them — and especially how to handle them in destination.php
. You'll see, it's super easy: instead of receiving a $_GET array, you'll now be working with a $_POST array containing the form data!
Text Fields
Text fields are the most common form element. You use them to ask for a name, username, or anything else that doesn't require fancy formatting.
Here's how you insert one in HTML:
<input type="text" />
For passwords, just use type="password" — it hides the input. Other than that, it works exactly the same way.
There are two important attributes you can (or must) use:
- name (required): This is the name of the field. Choose wisely, because it will determine the variable name in PHP. Example:
<input type="text" name="username" />
- value (optional): This sets the initial value of the field. By default, the field is empty, but it can be handy to pre-fill it. Example:
<input type="text" name="username" value="SiteRawFan" />
I know, you're starting to worry — "Where's the PHP?" Don't panic. It's simple and familiar. Whatever the user types will be available in destination.php through a variable like $_POST['username']
.
Let's do a quick demo: we'll make a form that asks for the visitor's first name and displays it proudly on the next page. So we'll have two separate files: the form page and the target page.
Here's the code for form.php:
<p> This page is pure HTML.<br /> Please enter your first name: </p><form action="destination.php" method="post"> <p> <input type="text" name="firstname" /> <input type="submit" value="Submit" /> </p> </form>
Just a reminder: the <input type="submit" />
creates the submit button that triggers the data to be sent — and thus the redirection to the target page.
Now let's build destination.php, which will retrieve the first name using the $_POST['firstname']
variable.
Here's the code for destination.php:
<p>Hello!</p><p>I know your name — mwahaha. You're called <?php echo $_POST['firstname']; ?>!</p>
<p>If you want to change your name, <a href="form.php">click here</a> to go back to the form.</p>
Textareas
Textareas (a.k.a. "multiline input fields") are like regular text fields... just bigger.
They're great for letting users write long messages.
Here's how you insert one in HTML:
<textarea name="message" rows="8" cols="45"> Your message here. </textarea>
Once again, the name attribute defines the variable name on the next page — so here, you'll get $_POST['message']
in destination.php.
Notice there's no value attribute. The default text goes between the opening and closing <textarea>
tags. If you want the field to start blank, just leave that part empty.
The rows and cols attributes control the height and width of the textarea, although you can use CSS too.
Drop-Down Lists
A dropdown list is just that — a menu where the user picks one of several options.
Here's how to make one:
<select name="choice"> <option value="choice1">Choice 1</option> <option value="choice2">Choice 2</option> <option value="choice3">Choice 3</option> <option value="choice4">Choice 4</option> </select>
We use the <select>
tag, give it a name (like "choice"), add a bunch of <option>
tags, and close it all with </select>
.
The variable $_POST['choice'] will contain the selected option's value. If the user picked "Choice 3", the value will be "choice3".
To set a default selection, add selected="selected"
to the corresponding option:
<option value="choice3" selected="selected">Choice 3</option>
Checkboxes
A checkbox is, well... a box you can check. Nothing too surprising there. 😄
Here's the HTML code:
<label for="box"><input type="checkbox" name="box" id="box" /> Check me</label>
<label>
is optional but highly recommended. It links the label to the checkbox via the for and id attributes, letting users click the label to check the box — better usability all around! For more info, see the form chapter in the HTML/CSS course.The name attribute defines the variable's name (e.g., $_POST['box']
).
- If the box is checked,
$_POST['box']
will equal "on". - If it's unchecked,
$_POST['box']
won't exist at all. You can useisset($_POST['box'])
to test if it was checked.
Want it checked by default? Just add checked="checked"
:
<input type="checkbox" name="box" checked="checked" />
Radio Buttons
Radio buttons come in groups of at least two. They're like checkboxes, except users can only choose one option.
Here's an example:
Do you like potato fries?<input type="radio" name="fries" value="yes" id="yes" checked="checked" /> <label for="yes">Yes</label> <input type="radio" name="fries" value="no" id="no" /> <label for="no">No</label>
As you can see, both radio buttons share the same name ("fries"). That's crucial: all radio buttons in the same group must have the same name, so the browser knows only one can be selected at a time. It would be silly to be able to pick both "Yes" and "No"! 😄
To pre-select one, just add checked="checked"
. Here, "Yes" is selected by default.
On the next page, you'll get a variable $_POST['fries']
containing the selected value. If the user picked "Yes," it will be "yes".
Always remember to set the value attribute — it determines the variable's value.
Hidden Fields
Hidden fields are a special kind of input. What do they do? Well, they don't show up on the screen, but they still send a variable with a value. You can use them to pass along fixed information.
Example: say you want to "remember" that the user's username is "SiteRawFan". You'd write:
<input type="hidden" name="username" value="SiteRawFan" />
On the webpage, the visitor sees nothing. But in destination.php, you'll get $_POST['username']
with the value "SiteRawFan"!
Sounds useless? Not at all — you'll see it's quite handy in some cases.
Never Trust User Input
Remember the warnings I gave you in the previous chapter about data passed from one page to another? They didn't just apply to URL parameters — the same goes for form data!
We often think visitors can't "fiddle" with forms — but that's just not true. First, I'll show you why forms aren't any safer than URLs, and then we'll dive into another big threat: the dreaded XSS vulnerability. After that, you'll be all set to browse in peace! :)
Why Forms Aren't Safe
Everything we covered in the last chapter about URLs still applies here. Anything coming from the user — that is, data in the $_GET or $_POST arrays — should be treated with extreme caution.
You cannot assume you'll receive the data you were expecting.
To check whether a string matches a certain format like MM/DD/YYYY, you can use a regular expression. We'll dedicate two full chapters to regex later on in this course since it's a fairly advanced topic.
Just like with URL parameters, if you ask for a number between 1 and 100, someone will inevitably type in "58451254529." So always be on guard and never trust anything that comes from the user, meaning anything from $_GET or $_POST.
And here's another thing: with forms, you can't even assume that you'll receive all the fields you're expecting. A visitor could very well decide to remove a text field entirely — and in that case, your destination.php page will never receive the value it was supposed to! So you must check that all the required data is present before doing anything with it.
Sure, your visitors can't change the web pages stored on your server... but nothing's stopping them from copying and tweaking those pages elsewhere. Let's go back to this setup:
form.php → destination.php
The form.php page holds your form, and destination.php processes the submitted data. While visitors can't see your PHP code, the HTML of the form is completely visible to everyone.
So what's stopping someone from grabbing your form's HTML, tweaking it slightly, and hosting it somewhere else?
https://www.siteraw.com/malicious_form.php → (your server's) destination.php
In the example above, the "bad guy" (let's just call him that) copied your form's HTML, modified it, and uploaded it to his server (or even just saved it locally). He changed the action attribute to the full URL of your destination page:
<form method="post" action="https://www.yoursite.com/destination.php">
Now, the bad guy can do whatever he wants: modify your form, add or remove fields, you name it! Your destination.php page will be none the wiser, because there's no way to reliably tell where the form submission came from (there is the HTTP referer header, but it can be spoofed).
This explanation is a bit technical — usually reserved for more advanced users — but I wanted you to see how it's possible. Even if it's not crystal clear right now, you've at least got the general idea.
There's an Even Easier Way to Modify Your Form...
You don't even need server access to mess with a site's form. Web browsers are just tools that send and receive HTTP(s) requests to servers. And once you understand that, it's easy for someone to mimic a browser's behavior — that's the whole idea behind bots.
Also, many browsers like Edge, Brave, and Safari come with "developer tools." With just a right-click on any page element, a user can modify it on the fly... no need to copy your form or host it elsewhere. They can just tweak it (locally, of course) right in their browser. Sure, they're the only one who sees the changes, but they can still alter the form's behavior, change a hidden input, and so on.
XSS Vulnerability: Watch Out for Malicious HTML!
One last danger to cover — and then I promise I'll stop scaring you! :)
XSS (short for cross-site scripting) has been around forever (well, since the early days of the Web :-° ) and is still found on lots of websites — even professional ones! The idea is to inject HTML code with JavaScript into your page, which then runs in the browser of whoever visits it.
Let's go back to that page that displays the user's first name. It might contain code like this:
<p>I know your name, hehe. Your name is <?php echo $_POST['firstName']; ?>!</p>
Now, if the visitor types HTML instead of their name, it'll still work! For example, they could enter <strong>BigBossMan</strong>
into the "First Name" field. The HTML output from PHP would look like this:
<p>I know your name, hehe. Your name is <strong>BigBossMan</strong>!</p>
So what? If they want their name in bold, that's their business, right?
Well, not quite. Beyond breaking your page's HTML (which isn't even the worst part), they could insert a <script>
tag to run JavaScript in the browser of anyone viewing the page!
<p>I know your name, hehe. Your name is <script>alert('BigBossMan');</script>!</p>
Everyone who visits that page would see a JavaScript pop-up. Annoying, to say the least.
Preventing XSS by Escaping HTML
Fortunately, the fix is simple: escape any HTML code, so it displays as text instead of being executed by the browser.
To escape HTML, use the htmlspecialchars function. It converts HTML brackets <
and >
into <
and >
, which makes the browser display them instead of running them.
<p>I know your name, hehe. Your name is <?php echo htmlspecialchars($_POST['firstName']); ?>!</p>
This will generate clean, safe HTML like:
<p>I know your name, hehe. Your name is <strong>BigBossMan</strong>!</p>
If you'd rather just remove the visitor's HTML entirely instead of escaping it, you can use the strip_tags function.
Uploading Files
Did you know forms can also be used to upload files? You'll want to read this section if you'd like your visitors to be able to upload images, programs, or any other kind of file to your site.
This part is a little trickier than the rest of the chapter. That said, it's not essential for understanding the rest of the course.
So don't hesitate to come back and read it later, when you actually need it, if you don't feel like diving into something slightly "tougher" just yet.
Once again, we're looking at a two-step process:
- The visitor arrives on your form and fills it out (choosing a file to upload). A simple HTML page is enough for this.
- PHP receives the form data and, if a file was uploaded, it "saves" the file in one of the server's folders.
Let's start by creating the form that allows file uploads (just a regular HTML page). We'll then see how to handle the upload on the server using PHP.
The File Upload Form
As soon as your form allows users to upload a file, you need to add the attribute enctype="multipart/form-data" to the <form> tag.
<form action="upload_target.php" method="post" enctype="multipart/form-data"> <p>File upload form</p> </form>
Thanks to enctype, the visitor's browser knows it's about to send files.
Now that this is in place, we can add the input field for uploading. It's a basic tag: <input type="file" />
. As always, make sure to give the input a name (via the name attribute) so that PHP can recognize it.
<form action="upload_target.php" method="post" enctype="multipart/form-data"> <p> File upload form:<br /> <input type="file" name="myfile" /><br /> <input type="submit" value="Upload the file" /> </p> </form>
And that's all there is to it! :)
You can of course add other regular form fields (text input, checkboxes, etc.). You can even allow multiple file uploads at once.
For now, we'll keep it simple with just one file field.
Handling the Upload with PHP
As you probably noticed, the form points to a PHP page called upload_target.php. That means the visitor will be redirected there after submitting the form.
Now comes the important part. We need to write the code for upload_target.php to handle the uploaded file.
When the PHP page runs, the file has indeed been uploaded, but only to a temporary folder. It's up to you to decide whether to accept the file permanently. For example, you might want to check if the file has the right extension — if you asked for an image and got a .txt file, you'll probably want to reject it.
If the file looks good, you can accept it permanently using the move_uploaded_file function.
For each uploaded file, PHP creates a variable $_FILES['field_name']
. In our case, that's $_FILES['myfile']
.
This is an array containing several pieces of info about the file:
Variable | Meaning |
---|---|
$_FILES['myfile']['name'] | The original name of the uploaded file. |
$_FILES['myfile']['type'] | The file type. For example, an image might be image/gif. |
$_FILES['myfile']['size'] | File size in bytes (1,000 bytes ≈ 1 KB, 1,000,000 bytes ≈ 1 MB). |
$_FILES['myfile']['tmp_name'] | The temporary location where PHP stored the file. |
$_FILES['myfile']['error'] | Error code (0 means no error occurred). |
If you added a second file field, you'd get another array like $_FILES['your_other_field']
, structured the same way.
Now let's define the checks we'll use to decide whether to accept the uploaded file:
- Check if a file was actually uploaded (isset($_FILES['myfile'])) and that no upload error occurred.
- Check that the file doesn't exceed 1 MB (around 1,000,000 bytes) using $_FILES['myfile']['size'].
- Check that the file extension is allowed (definitely block PHP files to avoid potential server exploits!). For our purposes, we'll allow only image files: .png, .jpg, .jpeg, and .gif.
We'll do all this in upload_target.php.
1. Check if a file was uploaded
We start by checking if $_FILES['myfile'] exists and that the error value is 0 (no error).
<?php // Check if file was uploaded and there's no error if (isset($_FILES['myfile']) && $_FILES['myfile']['error'] == 0) {} ?>
2. Check the file size
We want to block files over 1 MB. So we check the size property:
<?php // Check if file was uploaded and there's no error if (isset($_FILES['myfile']) && $_FILES['myfile']['error'] == 0) { // Check if file size is within the limit if ($_FILES['myfile']['size'] <= 1000000) {} } ?>
3. Check the file extension
<?php $fileInfo = pathinfo($_FILES['myfile']['name']); $uploadExtension = $fileInfo['extension']; ?>
The pathinfo function gives us an array that includes the file extension, which we store in $uploadExtension.
Then we check if this extension is in our list of allowed types using in_array().
Final result:
<?php if (isset($_FILES['myfile']) && $_FILES['myfile']['error'] == 0) { if ($_FILES['myfile']['size'] <= 1000000) { $fileInfo = pathinfo($_FILES['myfile']['name']); $uploadExtension = $fileInfo['extension']; $allowedExtensions = array('jpg', 'jpeg', 'gif', 'png'); if (in_array($uploadExtension, $allowedExtensions)) {} } } ?>
4. Finalize the upload
If everything checks out, we accept the file with move_uploaded_file().
This function takes two arguments:
- The temporary filename: $_FILES['myfile']['tmp_name'].
- The final destination path (you can keep the original name or generate a custom one).
We'll save the file into an uploads subdirectory using the original filename. Since $_FILES['myfile']['name'] might contain the full path (e.g., C:\folder\image.png), we'll use basename() to extract just the file name (image.png).
<?php if (isset($_FILES['myfile']) && $_FILES['myfile']['error'] == 0) { if ($_FILES['myfile']['size'] <= 1000000) { $fileInfo = pathinfo($_FILES['myfile']['name']); $uploadExtension = $fileInfo['extension']; $allowedExtensions = array('jpg', 'jpeg', 'gif', 'png'); if (in_array($uploadExtension, $allowedExtensions)) { // Validate and permanently store the file move_uploaded_file($_FILES['myfile']['tmp_name'], 'uploads/' . basename($_FILES['myfile']['name'])); echo "Upload completed successfully!"; } } } ?>
This script is a good start, but in practice, you'll likely need to improve it further. For instance, if the filename contains spaces or accented characters, it might cause problems once online. Also, if someone uploads a file with the same name as another user's, the older file will be overwritten!
The solution? Generate your own filenames — maybe something like a counter: 1.png, 2.png, 3.jpg, etc.
And that's a wrap on this chapter about PHP forms!
Now you know how things like forums, chat systems, and login pages are built. The real-world examples might be more complex than our simplified ones, but with what you've learned, you'll be just fine!
Enjoyed this PHP & MySQL course?
If you liked this lesson, you can find the book "How to Build a Website in HTML and CSS" from the same authors, available on SiteRaw, in bookstores and in online libraries in either digital or paperback format. You will find a complete PHP & MySQL workshop with many exclusive bonus chapters.