$_GET is an array of the URL's query string, so article.php?id=42&lang=en gives $_GET['id'] === '42' and $_GET['lang'] === 'en'. $_POST is an array of the fields of a form sent with method="post". Read both with a default, $_GET['id'] ?? null, because the key is missing whenever the parameter is.
Run the block, then click the links: each click runs the script again with $_GET filled from the link's query string.
$_GET['lang'] is whatever the URL says, not only the three links you printed. Anyone can type ?lang=<script> into the address bar, which is why the block looks the value up in $languages instead of printing it, and escapes the one place it does print request data.
Read a number from the query string
Every value in $_GET is a string. For a page number, validate and convert in one step with filter_var() and fall back to a default when the value is missing, not a number, or out of range. Click through the pages, then try the Page 999 and Page abc links.
filter_var() returns the integer 3 for "3" and the default option for any other string, so $page is always a usable int. The filter_var page lists the other validators.
Read form data with $_POST
A form with method="post" sends its fields in the request body, and PHP puts them in $_POST under each input's name. $_SERVER['REQUEST_METHOD'] tells you whether the current request is the first visit (GET) or a submit (POST). Fill in the form and press Send to see both.
An empty text input is still sent, as "", so after a submit $_POST['title'] exists even if the visitor typed nothing. On a server REQUEST_METHOD is always set; the ?? 'GET' only keeps the script quiet when you run the file with php on the command line. The full pattern for validating and redisplaying a form is on the forms page.
A search form with method="get"
Use GET for a form whose result should be a shareable URL, like a search box. The browser turns the fields into a query string (?q=sort&level=beginner) and the script reads them from $_GET, exactly like a link. Search for array or string.
Build links with http_build_query
Writing "?q=$q&page=2" by hand breaks as soon as $q contains a space, & or #. http_build_query() encodes every value for you, and the result goes through htmlspecialchars() when it lands in an href, because & should be & inside HTML.
parse_str() with two arguments is how PHP builds $_GET from a query string, so it is handy for testing. Always pass the second argument; the old one-argument form that created variables was removed in PHP 8.
$_REQUEST and why to avoid it
$_REQUEST is $_GET and $_POST merged into one array. The php.ini files PHP ships set request_order = "GP", so when both contain the same key, POST wins; without that setting, cookies are merged in too:
<?php
// POST /save.php?id=1 with a form field id=2
echo $_GET['id']; // "1"
echo $_POST['id']; // "2"
echo $_REQUEST['id']; // "2"
It looks convenient, but it hides where a value came from. A handler that deletes a record on $_REQUEST['id'] can be triggered by a plain link, which is exactly what POST-only actions are meant to prevent. Read $_GET or $_POST by name instead.
GET vs POST: which one to use
| GET | POST | |
|---|---|---|
| Where the data travels | In the URL: ?q=php&page=2 | In the request body |
| Read it with | $_GET | $_POST |
| Bookmark, share, back button | Yes | No (the browser asks to resend) |
| Length | Limited by the URL (a few thousand characters is safe) | Limited by post_max_size (8M by default) |
| File uploads | No | Yes, with enctype="multipart/form-data" (read $_FILES) |
| Use for | Search, filters, sorting, pagination | Login, sign up, saving, deleting, payments |
The rule that matters: a GET request must not change anything. Browsers prefetch links, crawlers follow them, and people share them, so a ?delete=5 link will eventually be followed by something that was not meant to delete. Neither method is encrypted on its own: HTTPS protects both, and POST only keeps the data out of the URL, the browser history and server logs.
Frequently Asked Questions
How do I get a parameter from the URL in PHP?
Read it from $_GET with a default: for page.php?id=42, $id = $_GET['id'] ?? null; gives the string "42". Every value is a string (or an array for id[]=), so convert and validate it, for example with filter_var($id, FILTER_VALIDATE_INT).
What is the difference between $_GET and $_POST?
$_GET comes from the URL's query string, so it is visible, bookmarkable and limited in length; use it for searches, filters and pages. $_POST comes from the request body of a form sent with method="post"; use it for anything that changes data, like saving, buying or logging in.
How do I check if a GET parameter exists in PHP?
Use isset($_GET['q']), which is false when the key is missing or null. To also treat an empty value (?q=) as missing, compare the trimmed value: trim($_GET['q'] ?? '') !== ''.
Should I use $_REQUEST in PHP?
Usually not. $_REQUEST merges $_GET and $_POST (and, depending on request_order, cookies), so you cannot tell where a value came from, and a link can supply a value you expected from a form. Read $_GET or $_POST explicitly.
How do I check if a request is GET or POST in PHP?
Compare $_SERVER['REQUEST_METHOD']: if ($_SERVER['REQUEST_METHOD'] === 'POST') { ... }. This is more reliable than isset($_POST['submit']), which fails when the button has no name or when the form is sent by JavaScript or another script without that field.