Introduction
Most useful PHP pages need input from somewhere. A visitor enters a name, selects an option, uploads a file, follows a link with values attached, or moves through several pages while the application remembers previous choices. The PHP script that receives those values is commonly called a destination page: it is the page that handles the submitted data and decides what happens next.
A form is usually shown in the browser, but the important PHP work happens after submission. The browser sends name and value pairs to a PHP script. PHP stores those pairs in system associative arrays such as $_POST, $_GET, $_FILES, $_REQUEST, and $_SESSION. Once you understand which array receives which data, form handling becomes much easier to reason about.
This tutorial focuses on the practical flow:
- Design the form fields and decide which values should be submitted.
- Receive submitted values in a PHP destination page.
- Validate required fields and numeric input.
- Handle optional controls such as checkboxes, radio groups, and multiple selections.
- Process uploaded files through
$_FILES. - Pass values through an extended web address using
$_GET. - Use sessions when values must survive across several PHP pages.
Context and Scope
A PHP input flow normally has these parts:
User fills in a browser form
|
v
Browser submits field names and values
|
v
Destination PHP page receives the values
|
v
PHP validates, transforms, stores, or displays the data
|
v
Application continues to another page, saves a file, or updates state
The browser form defines the names of the values. The destination page reads those names. For example, if a form field is named firstName, the destination page can read its value from $_POST['firstName'] when the form uses the POST method.
For a practical mental model, think of the form as a contract:
Form contract
Destination page: register-member.php
Method: POST
Fields sent by the browser:
- firstName: text value, required
- lastName: text value, required
- address: longer text value, required
- ageBand: one selected radio value, optional unless validated
- country: one selected list value
- acceptedTerms: checkbox value, only sent when checked
- submitButton: submit button value
The PHP destination page should not assume that every expected value exists. Some controls send empty strings when left blank. Others send nothing at all when no choice is made. That difference matters when you validate input.
Common Form Objects and What They Send
A web form can contain several different kinds of form object. Each object has a name. That name becomes the key used by PHP in $_POST or another input array.
| Form object | Typical purpose | What PHP receives |
|---|---|---|
| Text field | One-line input such as first name or price | A string, possibly empty |
| Textarea | Multi-line input such as address or notes | A string, possibly empty |
| Radio group | One choice from a group | The selected value, or no value if nothing is selected |
| Select box | One choice from a list | The selected value |
| Multiple select box | Several choices from a list | An array of selected values when configured as an array field |
| Checkbox | On or off choice | A value only if checked |
| Password field | Password-like input | A string value, even though the browser masks it visually |
| Hidden field | Application value not shown on the page | A string value, but not a secret |
| File field | User-selected file | Metadata and temporary file information in $_FILES |
| Submit button | Sends the form | Often sends its own name and value too |
A hidden field is useful for values that the application needs but the user does not need to edit, such as a project reference. It should not be treated as secret. A user can view the page source and see hidden field values.
A password field only hides typed characters on the screen. After submission, PHP receives the password value as ordinary text. Do not assume that a password field protects the submitted value after it reaches your destination script.
Read-only and disabled fields also behave differently. A read only field cannot be edited by the user, but its value is still submitted. A disabled field is visually inactive and its value is not submitted. If you need to display an uneditable value and still submit it, use a read only field or pair the disabled display with another submitted value.
Reading POST Data in a Destination Page
When a form submits using the POST method, PHP receives the submitted values in the $_POST associative array. The keys are the form field names. The values are the submitted field values.
A useful first debugging page is one that prints everything received:
<?php
foreach ($_POST as $fieldName => $fieldValue) {
print "POST item: " . $fieldName . " => " . $fieldValue . PHP_EOL;
}
?>
For a real destination page, you usually copy the incoming values into ordinary variables. That makes the rest of the script easier to read.
<?php
$firstName = $_POST['firstName'];
$lastName = $_POST['lastName'];
$address = $_POST['address'];
print "Received member: " . $firstName . " " . $lastName . PHP_EOL;
?>
This code assumes that the form always sends those fields. For text fields, password fields, and textareas, PHP normally receives an empty string when the user leaves the field blank. For controls such as radio groups, checkboxes, and some select boxes, the value may not exist at all when the user makes no selection.
That is why isset() is useful for some fields but not enough for others.
Validating Required Fields
Adding an asterisk next to a field in the browser is only a visual convention. It does not enforce anything by itself. The destination page must check required values before continuing.
For text fields and textareas, trim the input first. Users often add accidental spaces before or after a value. A value that contains only spaces should usually be treated as empty.
<?php
$firstName = trim($_POST['firstName']);
$lastName = trim($_POST['lastName']);
$address = trim($_POST['address']);
if ($firstName == "" || $lastName == "" || $address == "") {
print "Error: first name, last name, and address are required.";
} else {
print "The required values were provided.";
}
?>
For a radio group, checkbox, or optional select box, isset() can tell you whether the browser sent the value at all.
<?php
if (isset($_POST['ageBand'])) {
$ageBand = $_POST['ageBand'];
print "Selected age band: " . $ageBand;
} else {
print "No age band was selected.";
}
?>
Do not use isset() alone to check whether a text field has useful content. A blank text field can still be considered set. For text input, test the trimmed string instead.
Handling Textareas and Line Breaks
A textarea can send multiple lines of text. Internally, line breaks may be represented by carriage return and linefeed characters. In PHP string notation, these are commonly written as \r and \n.
When displayed directly in a browser page, ordinary whitespace and line breaks are often collapsed visually. The submitted value still contains the line break information, but the browser may not show it as separate lines unless you format it for browser display.
For plain text processing, you can normalize textarea line breaks before storing or printing them:
<?php
$notes = $_POST['notes'];
$notes = str_replace("\r\n", "\n", $notes);
print $notes;
?>
This gives you a predictable internal representation. The right display format depends on how your destination page presents output.
Checking Numeric Input
Values submitted from a form arrive as strings. Even if the user types 89, PHP receives it as a string value. That is why numeric validation needs care.
Use is_numeric() when a field should contain a number.
<?php
$price = trim($_POST['price']);
if (is_numeric($price)) {
print "Price is numeric: " . $price;
} else {
print "Price must be numeric.";
}
?>
The numeric check accepts values that PHP considers valid numbers, including signed values, decimal values, and exponent notation. Trimming matters because trailing spaces can cause a value to be treated as non-numeric.
Checking for integers has one extra trap. A submitted value such as 89 is still a string, so is_int($price) will not behave as many beginners expect. One simple way to convert a numeric string into a number is to use it in an arithmetic operation.
<?php
$quantity = trim($_POST['quantity']);
if (is_numeric($quantity)) {
$converted = $quantity * 1;
if (is_int($converted)) {
print "Quantity is an integer: " . $converted;
} else {
print "Quantity is numeric but not an integer.";
}
} else {
print "Quantity must be numeric.";
}
?>
The practical lesson is simple: validate the string first, then convert it when you need numeric behavior.
Working with Radio Buttons, Select Boxes, and Checkboxes
Radio groups are used when the user should choose one option from several. Every button in the same group shares the same field name, but each button can submit a different value. The destination page receives the value of the selected button.
A checkbox is different. If the checkbox is checked, PHP receives its configured value. If it is not checked, PHP may receive nothing for that name.
<?php
if (isset($_POST['acceptedTerms'])) {
print "The user accepted the terms.";
} else {
print "The user did not accept the terms.";
}
?>
A select box normally sends one selected value. If the list allows multiple selections, the form field should be designed as an array field, commonly using square brackets in the field name, such as interests[]. PHP then receives an array of selected values.
Before looping through a multiple selection, check that something was sent. If no option was selected, there may be no value to loop over.
<?php
if (isset($_POST['interests'])) {
$selectedInterests = $_POST['interests'];
foreach ($selectedInterests as $position => $interestCode) {
print "Selected interest " . $position . ": " . $interestCode . PHP_EOL;
}
} else {
print "No interests were selected.";
}
?>
When multiple values are received, the order is based on the order of the options in the form, not necessarily the order in which the user clicked them.
Generating Repetitive Form Controls with PHP
Long selection lists are tedious to write manually. A date-of-birth form might need values from 1 to 31 for the day, 1 to 12 for the month, and a large range of years. A country list might contain many entries. PHP can generate those repetitive pieces from a small amount of input.
The idea is to separate data from generation logic:
Data source:
January => 1
February => 2
March => 3
Generator responsibility:
- create one option per data item
- mark a default item when needed
- keep the submitted value separate from the displayed label
You can also read a list from a plain text file where each line contains a label and a code separated by a character such as *.
Sweden*SE
Norway*NO
Denmark*DK
Finland*FI
A PHP function can then read each line, trim it, split it with explode(), and generate the necessary form options. This avoids manually maintaining a large block of repetitive form markup.
<?php
function loadChoicePairs($filename) {
$lines = file($filename);
$choices = array();
for ($i = 0; $i < count($lines); $i++) {
$line = trim($lines[$i]);
$parts = explode("*", $line);
$choices[$parts[0]] = $parts[1];
}
return $choices;
}
$countries = loadChoicePairs("countries.txt");
foreach ($countries as $label => $code) {
print $label . " uses code " . $code . PHP_EOL;
}
?>
This example prints the loaded values rather than building browser markup, but the same pattern is useful when a PHP page needs to generate form controls automatically.
Handling Uploaded Files with FILES
File upload handling is different from ordinary form fields. Ordinary values arrive in $_POST. Uploaded file information arrives in $_FILES.
For a file field named reportFile, PHP provides values such as:
| Key | Meaning |
|---|---|
name |
Original filename supplied by the browser |
type |
Reported content type, often based on the extension |
tmp_name |
Temporary server-side file path |
error |
Upload error code |
size |
Uploaded file size in bytes |
A small inspection script helps you understand what was received:
<?php
foreach ($_FILES['reportFile'] as $itemName => $itemValue) {
print "File item: " . $itemName . " => " . $itemValue . PHP_EOL;
}
?>
A submitted file is first held as a temporary file on the server. Your script must move it to a permanent location if you want to keep it. The move_uploaded_file() function is used for that step.
<?php
$filename = $_FILES['reportFile']['name'];
$temporaryName = $_FILES['reportFile']['tmp_name'];
$targetPath = "projects/" . $filename;
if (move_uploaded_file($temporaryName, $targetPath)) {
print "The uploaded file was saved.";
} else {
print "The uploaded file could not be saved.";
}
?>
Several things can make a file upload fail:
- The form was not submitted with the correct method for file uploads.
- The form did not use the required multipart encoding for file uploads.
- The uploaded file exceeded a server-level size limit.
- The upload target directory is not writable.
- The application-level file size limit was exceeded.
You can check whether a target folder is writable before trying to move the file:
<?php
if (is_writable("projects")) {
print "The target folder is writable.";
} else {
print "The target folder is not writable.";
}
?>
The reported type value is useful, but it should not be treated as perfect proof of file content. For example, a PDF file might be reported as application/pdf, but this primarily reflects what the upload says about the file type. PHP also provides mime_content_type() as a practical check of the likely file type.
<?php
$temporaryName = $_FILES['reportFile']['tmp_name'];
$detectedType = mime_content_type($temporaryName);
if ($detectedType == "application/pdf") {
print "The file appears to be a PDF.";
} else {
print "The file is not the expected type.";
}
?>
File uploads create security risk because the user is sending content to your server. At minimum, check size, expected type, and whether the destination directory can be written. More advanced file security requires deeper handling beyond basic form processing.
Dealing with Quotes in User Input
Text fields, password fields, and textareas can contain single quotes and double quotes. PHP receives those characters as part of the submitted string.
That can become a problem when input is later stored or processed by another system. A practical defensive transformation is to replace straight quote characters with a backtick character before continuing.
<?php
$nickname = $_POST['nickname'];
$nickname = str_replace("\"", "`", $nickname);
$nickname = str_replace("'", "`", $nickname);
print $nickname;
?>
Apply the same idea to every free-text field that can contain quotes when your application needs to avoid quote-related processing problems later.
Passing Values Through an Extended Web Address
Forms are not the only way to pass values to a PHP script. A link can include values directly in the web address. The part after the question mark contains name and value pairs separated by ampersands.
profile.php?member=Erin Bryce&mode=edit&level=3
In that example, the destination page is profile.php. The values are available through $_GET:
<?php
$member = $_GET['member'];
$mode = $_GET['mode'];
$level = $_GET['level'];
print "Member: " . $member . PHP_EOL;
print "Mode: " . $mode . PHP_EOL;
print "Level: " . $level . PHP_EOL;
?>
You can inspect everything received through an extended address like this:
<?php
foreach ($_GET as $name => $value) {
print "GET item: " . $name . " => " . $value . PHP_EOL;
}
?>
An extended address is useful when the value naturally belongs in a link or redirect, such as an item identifier, a mode value, or a small filter setting. It is less suitable for large amounts of data.
Values in an extended address should not contain ampersands because ampersands separate one name and value pair from the next. Quotes, if used, are treated as actual characters.
Combining POST and GET with REQUEST
Sometimes a destination page can receive values from both a submitted form and an extended address. PHP provides $_REQUEST as a combined view of values from $_POST and $_GET.
<?php
foreach ($_REQUEST as $name => $value) {
print "REQUEST item: " . $name . " => " . $value . PHP_EOL;
}
?>
In the setup demonstrated here, if the same name appears in both POST data and GET data, the POST value is the one that appears in $_REQUEST. That means $_REQUEST can be convenient, but it can also hide where a value came from.
A simple rule works well for beginners:
- Use
$_POSTwhen you expect a submitted form value. - Use
$_GETwhen you expect a value from the web address. - Use
$_REQUESTonly when the same destination page intentionally accepts either source.
Being explicit makes scripts easier to understand and debug.
Using Sessions for Multi-Page Workflows
Passing values through hidden fields or extended web addresses can become messy when a user moves through many pages. A shopping flow, registration wizard, or multi-step admin process may need to remember several values while the user moves forward and backward.
PHP sessions solve this by storing values on the server in the $_SESSION associative array. The user device only needs to carry a session identifier. The PHP pages that participate in the same session can read, change, add, and remove session values.
Every session-aware page must call session_start() before any output is sent.
A first page can start the session and store values:
<?php
session_start();
$_SESSION['staffName'] = "John Smith";
$_SESSION['staffAge'] = 47;
$_SESSION['staffTitle'] = "Lecturer";
foreach ($_SESSION as $name => $value) {
print $name . " => " . $value . PHP_EOL;
}
?>
A later page can read existing session values and add another one:
<?php
session_start();
print "Age: " . $_SESSION['staffAge'] . PHP_EOL;
print "Title: " . $_SESSION['staffTitle'] . PHP_EOL;
$_SESSION['companyRef'] = "worldcorp";
foreach ($_SESSION as $name => $value) {
print $name . " => " . $value . PHP_EOL;
}
?>
Another page can remove one session value with unset():
<?php
session_start();
$_SESSION['staffCode'] = "Sci387";
unset($_SESSION['staffTitle']);
foreach ($_SESSION as $name => $value) {
print $name . " => " . $value . PHP_EOL;
}
?>
The final page in the workflow can clear the session values and destroy the session:
<?php
session_start();
session_unset();
session_destroy();
print "The session has ended.";
?>
The placement of session_start() is critical. It must happen before the page sends any normal output. That includes text printed by PHP and ordinary page content outside PHP.
Practical Destination Page Workflow
A reliable PHP destination page can follow this structure:
- Start the session if the page needs session values.
- Read expected values from
$_POST,$_GET, or$_REQUEST. - Trim user-entered text values.
- Check required fields.
- Check optional controls with
isset()before reading them. - Validate numeric fields with
is_numeric()before using them as numbers. - Normalize or transform free-text values when needed.
- Inspect
$_FILESfor uploads. - Validate file size and likely file type.
- Move uploaded files to a permanent location if they should be kept.
- Store, display, or pass the processed values to the next step.
Here is a compact example that combines several of those ideas:
<?php
session_start();
$studentNumber = trim($_POST['studentNumber']);
$password = trim($_POST['studentPassword']);
$projectRef = $_POST['projectRef'];
if ($studentNumber == "" || $password == "") {
print "Student number and password are required.";
} else {
$_SESSION['studentNumber'] = $studentNumber;
$_SESSION['projectRef'] = $projectRef;
if (isset($_FILES['projectReport'])) {
$detectedType = mime_content_type($_FILES['projectReport']['tmp_name']);
if ($detectedType == "application/pdf") {
$filename = $projectRef . "-" . $studentNumber . ".pdf";
$targetPath = "projects/" . $filename;
if (move_uploaded_file($_FILES['projectReport']['tmp_name'], $targetPath)) {
print "Project report saved.";
} else {
print "Project report could not be saved.";
}
} else {
print "Project report must be a PDF file.";
}
} else {
print "Project report is required.";
}
}
?>
This example uses a project reference and student number to create a saved filename. It also checks that the uploaded file appears to be a PDF before moving it into the projects folder.
Common Mistakes to Watch For
Treating visual form hints as validation
An asterisk next to a field only tells the user that the value is expected. The destination page still needs to check it.
Using isset() for blank text fields
A blank text field can still be set. Use trim() and compare with an empty string for text fields, password fields, and textareas.
Forgetting that unchecked boxes may not exist
Checkboxes, radio groups, and some select boxes may send no key at all. Use isset() before reading them.
Expecting form numbers to arrive as numbers
Submitted values arrive as strings. Validate with is_numeric() and convert before numeric use.
Assuming password fields stay hidden after submission
Password fields mask input on the user's screen. PHP receives the value as plain text.
Treating hidden fields as secret
Hidden fields are not displayed in the form, but they can still be seen by users who inspect the page.
Forgetting special handling for file uploads
Uploaded files are not read from $_POST. Use $_FILES, inspect the temporary file, and move it if it should be saved.
Starting a session too late
session_start() must run before any output is sent.
Overusing $_REQUEST
$_REQUEST can be convenient, but it makes the input source less obvious. Prefer $_POST or $_GET when you know where the value should come from.
Checklist
Use this checklist when building a PHP form flow:
- Decide which PHP page receives the form.
- Give every field a clear, unique name.
- Use POST for normal form submission.
- Use array-style names for multiple selections.
- Treat hidden fields as convenience values, not secrets.
- Use
$_POSTfor submitted form values. - Use
$_GETfor values passed in the address. - Use
$_FILESfor uploaded file data. - Use
$_SESSIONwhen values must survive across pages. - Trim text input before validation.
- Use
isset()for optional controls that may not be sent. - Validate required fields in the destination page.
- Use
is_numeric()before numeric conversion. - Check uploaded file size and likely type.
- Confirm the upload target folder is writable.
- Call
session_start()before output on every session page. - Clear or destroy sessions when the workflow is finished.
Conclusion
Passing values into PHP is mainly about knowing where PHP stores each kind of input. Submitted form fields usually arrive in $_POST. Values attached to a web address arrive in $_GET. Uploaded files arrive in $_FILES. Combined values can be inspected through $_REQUEST, and multipage state belongs in $_SESSION.
Once that separation is clear, the rest of the work is practical discipline: trim text, validate required fields, check optional controls before reading them, treat uploaded files carefully, and start sessions before any output. These habits make PHP destination pages easier to debug and much safer to extend as the application grows.