Superglobals are the doorway between the outside world and your PHP code.
| Superglobal | Who controls it |
|---|---|
$_GET, $_POST, $_REQUEST | The visitor |
$_COOKIE | The visitor |
$_FILES | The visitor (name, type, content) |
$_SERVER (HTTP_* keys) | The visitor |
$_SERVER (other keys), $_ENV | The server |
$_SESSION | Your code (stored on the server) |
Almost everything that goes wrong with web security starts with trusting a value from the top half of that table.
Three habits
1. Read with a default.
<?php
$_GET = [];
$page = $_GET["page"] ?? 1;
echo $page;
1
2. Validate before you use.
<?php
$_GET = ["page" => "-4"];
$page = filter_var($_GET["page"] ?? 1, FILTER_VALIDATE_INT, ["options" => ["min_range" => 1, "default" => 1]]);
echo $page;
1
3. Escape when you output.
<?php
$_GET = ["name" => "<b>Bob</b>"];
echo "Hello, " . htmlspecialchars($_GET["name"] ?? "");
Hello, <b>Bob</b>
One place to read input
When every page reads $_POST directly, mistakes spread. A small helper keeps the rules in one spot.
<?php
final class Input
{
public function __construct(private array $data) {}
public function string(string $key, string $default = ""): string
{
$value = $this->data[$key] ?? $default;
return is_string($value) ? trim($value) : $default;
}
public function int(string $key, int $default = 0): int
{
$value = filter_var($this->data[$key] ?? null, FILTER_VALIDATE_INT);
return $value === false ? $default : $value;
}
}
$input = new Input(["name" => " Isha ", "age" => "29x"]);
echo "[" . $input->string("name") . "] ";
echo $input->int("age", -1);
[Isha] -1
Frameworks such as Laravel give you a “request” object that works like this, so the habit carries over.
Do not write into superglobals
Changing $_GET or $_POST makes code hard to follow. Copy the value into a variable and change the variable.
filter_input()
filter_input() reads straight from the original request and validates in one step.
<?php
$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT); // int, false or null
It returns null when the variable is missing and false when the value fails the filter. That is easy to forget, so many developers prefer a helper like the one above.