Superglobals ($_GET, $_POST, $_SERVER)
Reviewed & published by Brayan K
This is where PHP becomes a web language. Superglobals are the built-in arrays — $_GET, $_POST, $_SERVER and friends — that carry data from the browser into your script. You'll learn to read them, default them, and most importantly, treat them as untrusted.
Part of the free PHP course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- Know the main superglobals and what each holds
- Read URL data from $_GET and form data from $_POST
- Use ?? to provide safe defaults for missing keys
- Inspect the request with $_SERVER
- Validate input with filter_var and whitelists
- Prevent XSS by escaping output with htmlspecialchars
1️⃣ The Superglobal Family
PHP fills several built-in arrays for you on every request, each available in every scope. You'll use $_GET, $_POST, and $_SERVER constantly, with $_SESSION, $_COOKIE, and $_FILES arriving in later lessons. Because they're arrays, you always read them by key.
<?php
// Superglobals are built-in arrays PHP fills for you on every request.
// They are GLOBAL: available in every scope without 'global'.
//
// $_GET — data from the URL query string (?key=value)
// $_POST — data from a submitted <form method="post">
// $_REQUEST — $_GET + $_POST + $_COOKIE merged (avoid; ambiguous)
// $_SERVER — info about the request and server environment
// $_COOKIE — cookies sent by the browser
// $_SESSION — per-user server-side storage (after session_start)
// $_FILES — uploaded files
// $_ENV — environment variables
// They are always ARRAYS, so you read them by key.
echo gettype($_GET) . "\n"; // array
echo gettype($_SERVER) . "\n"; // array
?>2️⃣ Reading $_GET (URL Data)
$_GET holds the URL's query-string parameters. Two things to internalise: keys may be missing (so always supply a default with ??), and every value arrives as a string (so cast when you need a number).
<?php
// Imagine the URL is: /search.php?q=php&page=2
// PHP fills $_GET = ['q' => 'php', 'page' => '2'].
// NOTE: every value is a STRING, even "2".
// The server fills $_GET from that query string. Set it here so the snippet
// runs on its own and prints exactly what that URL would give you.
$_GET = ['q' => 'php', 'page' => '2'];
// NEVER read $_GET['q'] blindly — it may be missing. Use ?? for a default.
$query = $_GET['q'] ?? '';
$page = (int) ($_GET['page'] ?? 1); // cast to int after defaulting
echo "Searching for: $query\n"; // Searching for: php
echo "On page: $page\n"; // On page: 2
echo "Type of page: " . gettype($page) . "\n"; // integer
?>3️⃣ Reading $_POST (Form Data)
$_POST carries fields from a form submitted with method="post". The discipline is the same — default missing keys, trim text — plus a non-negotiable step: validate before you trust anything. filter_var() is your friend here.
<?php
// $_POST holds form fields from a POST submission, e.g.:
// <form method="post">
// <input name="email"> <input name="age"> ...
// </form>
// Suppose the user submitted email=ada@x.com and age=29 — which, outside a
// real POST request, means setting it yourself:
$_POST = ['email' => 'ada@example.com', 'age' => '29'];
$email = trim($_POST['email'] ?? '');
$ageRaw = $_POST['age'] ?? '';
// Validate before trusting ANY of it.
$emailOk = filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
$age = filter_var($ageRaw, FILTER_VALIDATE_INT);
echo $emailOk ? "Email valid\n" : "Email invalid\n"; // Email valid
echo ($age !== false) ? "Age: $age\n" : "Bad age\n"; // Age: 29
?>4️⃣ Inspecting the Request: $_SERVER
$_SERVER describes the request and environment — the HTTP method, host, requested URI, client IP, headers, and more. The most common use is branching on REQUEST_METHOD to decide whether to show a form or process a submission.
<?php
// $_SERVER describes the current request and environment.
// A web server fills it; on the command line you fill it yourself, so these
// four lines stand in for one typical POST request.
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['REQUEST_URI'] = '/search?q=php';
$_SERVER['REMOTE_ADDR'] = '203.0.113.7';
echo $_SERVER['REQUEST_METHOD'] . "\n"; // GET or POST
echo $_SERVER['HTTP_HOST'] . "\n"; // example.com
echo $_SERVER['REQUEST_URI'] . "\n"; // /search?q=php
echo $_SERVER['REMOTE_ADDR'] . "\n"; // the client IP
// A common pattern: branch on the HTTP method.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
echo "Handle form submission\n";
} else {
echo "Show the form\n";
}
?>5️⃣ The Golden Rule: Never Trust Input ⚠️
Every value in a superglobal came from the client and can be forged. Two attacks dominate: XSS (echoing raw input lets an attacker inject scripts) and injection (putting raw input into SQL or shell commands). The defences: escape on output with htmlspecialchars(), validate or whitelist input, and use prepared statements for databases (covered later).
<?php
// ⚠️ GOLDEN RULE: superglobal data is UNTRUSTED user input.
// Never echo it raw (XSS) and never put it in SQL by hand (injection).
$name = $_GET['name'] ?? 'guest';
// XSS-safe output: escape with htmlspecialchars before echoing to HTML.
echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . "\n";
// If $name was <script>, it prints <script> — harmless text.
// Validate/whitelist instead of trusting:
$sort = $_GET['sort'] ?? 'name';
$allowed = ['name', 'date', 'price'];
$sort = in_array($sort, $allowed, true) ? $sort : 'name';
echo "Sorting by: $sort\n";
?>Now you try. Fill the ___ blanks to read query params safely.
<?php
// 🎯 YOUR TURN — fill each ___ to read query params safely.
// Pretend the URL is: /list?category=books&limit=5
$category = $_GET['category'] ___ 'all'; // 👉 use ?? for a default
$limit = (___) ($_GET['limit'] ?? 10); // 👉 cast to int
echo "Category: $category\n"; // Category: books
echo "Limit: $limit\n"; // Limit: 5
// ✅ Expected output:
// Category: books
// Limit: 5
?>🧩 Reorder Challenge
These scrambled lines should safely read and escape a name from the query string. Put them in the order that runs correctly (assume $_GET['name'] = 'Ada').
First read the value with a ?? default so a missing key can't error, then trim it, then escape it for safe HTML output, and only then echo. Escaping must happen before output — that's what stops XSS. Output: Hello, Ada.
🧠 Quick Recall
Predict the output before revealing each answer.
Prints string. All query and form values arrive as strings — cast with (int) when you need a number.
Prints none. The key is missing, so the null-coalescing operator falls back to the default instead of raising an "Undefined array key" warning.
Prints <b>hi</b> — the angle brackets are escaped, so the browser shows literal text instead of running it. This is the core XSS defence.
Common Errors (and the fix)
- "Warning: Undefined array key" — you read a key that wasn't sent. Default it with $_GET['k'] ?? 'fallback'.
- A number from $_POST behaves like text — it is a string. Validate then cast: (int) or filter_var.
- User input renders as live HTML/script — you echoed it raw (XSS). Wrap output in htmlspecialchars().
- $_POST is empty after submit — the form is missing method="post", or you're reading $_GET by mistake.
Pro Tips
- 💡 Default every superglobal read with ??. Keys are optional; assume they can be absent.
- 💡 Validate on input, escape on output. Two habits that stop most web vulnerabilities cold.
- 💡 Prefer specific superglobals over $_REQUEST. Reading $_POST explicitly is clearer and safer.
📋 Quick Reference — Superglobals
| Superglobal | Holds | Typical use |
|---|---|---|
| $_GET | URL query params | Search, filters, paging |
| $_POST | Form body | Sign-ups, comments |
| $_SERVER | Request/env info | Method, host, IP |
| $_COOKIE | Browser cookies | Preferences |
| $_SESSION | Server-side state | Login state |
| $_FILES | Uploads | Avatars, docs |
| $_REQUEST | GET+POST+COOKIE | Avoid — ambiguous |
Mini-Challenge: Contact Form Handler
Write it yourself, then check against the expected output in the comments. To run it live, serve the file and POST the form data described.
<?php
// 🎯 MINI-CHALLENGE: a safe "contact form" handler.
// No code filled in — follow the steps, then run and compare.
// Assume $_POST = ['name' => ' Ada ', 'email' => 'ada@x.com'].
//
// 1. Read $_POST['name'] with ?? '' and trim() it.
// 2. Read $_POST['email'] with ?? ''.
// 3. Validate the email with filter_var(..., FILTER_VALIDATE_EMAIL).
// 4. If valid, echo: Thanks <name>, we'll reply to <email>
// using htmlspecialchars() on the name.
// Otherwise echo: Please enter a valid email.
//
// ✅ Expected output:
// Thanks Ada, we'll reply to ada@x.com
// your code here
?>🎉 Lesson Complete!
- ✅ Superglobals are built-in arrays PHP fills on every request
- ✅ $_GET reads URL data; $_POST reads form data — both as strings
- ✅ Always default missing keys with ??
- ✅ $_SERVER describes the request; branch on REQUEST_METHOD
- ✅ Validate on input, escape with htmlspecialchars on output
Practice quiz
For the URL ?page=2, what does gettype($_GET['page']) return?
- 'integer'
- 'array'
- 'string'
- 'double'
Answer: 'string'. Every $_GET and $_POST value arrives as a string — cast with (int) when you need a number.
With no ?q in the URL, what does echo $_GET['q'] ?? 'none'; print?
- none
- An 'Undefined array key' warning
- null
- q
Answer: none. The key is missing, so ?? falls back to 'none' without raising an undefined-key warning.
Which superglobal holds data from a submitted <form method="post">?
- $_GET
- $_SERVER
- $_COOKIE
- $_POST
Answer: $_POST. $_POST carries form-body fields; $_GET carries the URL query string.
What does htmlspecialchars('<b>hi</b>') output?
- <b>hi</b>
- <b>hi</b>
- hi
- bold hi
Answer: <b>hi</b>. The angle brackets are escaped so the browser shows literal text instead of running it — the core XSS defence.
Which superglobal would you read REQUEST_METHOD from?
- $_SERVER
- $_GET
- $_POST
- $_SESSION
Answer: $_SERVER. $_SERVER describes the request and environment, including REQUEST_METHOD, HTTP_HOST, and REMOTE_ADDR.
Why should you avoid $_REQUEST?
- It is slower than $_GET
- It does not exist in PHP 8
- It ambiguously merges GET, POST, and COOKIE
- It only holds strings
Answer: It ambiguously merges GET, POST, and COOKIE. $_REQUEST merges $_GET, $_POST, and $_COOKIE; you can't tell the source, and a cookie could override a form field.
What is the safest way to handle a 'sort' parameter from the user?
- Echo it directly into the SQL query
- Whitelist it against an allowed list with in_array
- Trust it because it came over HTTPS
- Cast it to int
Answer: Whitelist it against an allowed list with in_array. Validate or whitelist input: check it against ['name','date','price'] and fall back to a safe default.
How should you treat all superglobal data?
- As safe because PHP fills it
- As already escaped
- As always numeric
- As untrusted user input that can be forged
Answer: As untrusted user input that can be forged. Everything in $_GET, $_POST, $_COOKIE and most of $_SERVER comes from the client and can be forged.
After $page = (int) ($_GET['page'] ?? 1); what is gettype($page)?
- 'string'
- 'integer'
- 'double'
- 'array'
Answer: 'integer'. The (int) cast converts the defaulted value to an integer.
Which pair of habits stops most web vulnerabilities?
- Echo raw, trust everything
- Use $_REQUEST everywhere
- Validate on input, escape on output
- Skip defaults to fail fast
Answer: Validate on input, escape on output. Validate or whitelist input and escape output with htmlspecialchars — two habits that block injection and XSS.
Continue this course
- Previous: Constants
- Next: Null & Null-Safe Operators — Handle missing values cleanly with ??, ??=, and the ?-> null-safe operator
- Quick reference: PHP cheat sheet
Frequently asked questions
What are superglobals in PHP?
Superglobals are built-in associative arrays that PHP populates automatically for every request and makes available in every scope without the global keyword. The main ones are $_GET (URL query data), $_POST (form data), $_SERVER (request and server info), $_COOKIE, $_SESSION, $_FILES (uploads), $_ENV, and $_REQUEST. They are always arrays, so you access individual pieces by key, and every value from $_GET and $_POST arrives as a string.
What is the difference between $_GET and $_POST?
$_GET carries data in the URL's query string (?q=php&page=2), so it's visible, bookmarkable, length-limited, and suited to navigation and search where the request just reads data. $_POST carries data in the request body from a form submitted with method="post"; it isn't shown in the URL, has no practical size limit, and is the right choice for actions that change state — creating accounts, posting comments, processing payments. Neither is secure on its own; both need validation, and sensitive submissions need HTTPS.
Why must I never trust superglobal data?
Everything in $_GET, $_POST, $_COOKIE, and most of $_SERVER comes from the client and can be forged. Echoing it raw into a page enables cross-site scripting (XSS), and concatenating it into SQL enables injection. The defences are: escape on output with htmlspecialchars() before printing to HTML, use prepared statements for database queries, and validate or whitelist input with filter_var() and in_array() rather than accepting whatever arrives. Treat every superglobal value as hostile until proven safe.
Should I use $_REQUEST?
Generally no. $_REQUEST merges $_GET, $_POST, and $_COOKIE into one array, which is convenient but ambiguous — you can't tell where a value came from, and the merge order is configurable, so a cookie could silently override a form field. Read from the specific superglobal you mean ($_GET or $_POST). Being explicit makes your code clearer and closes a subtle class of spoofing bugs.