Skip to main content

🌐 Lesson 14: Superglobals

Every time a browser sends a request to your PHP script, PHP automatically populates a set of special arrays with information about the request, the server, the environment, and more. These are superglobals — arrays that are accessible from anywhere in your code, in any scope, without needing global declarations.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain what superglobals are and why they're always in scope
  • Use $_SERVER to access request metadata, paths, and client information
  • Understand the relationship between $_GET, $_POST, and $_REQUEST
  • Inspect $_FILES for uploaded file information
  • Access environment variables with $_ENV and getenv()
  • Preview $_COOKIE and $_SESSION (covered in depth in Lesson 15)
  • Use $GLOBALS to reference global variables from any scope

Estimated Time: 40 minutes

Prerequisites: Lessons 11–13 (forms, validation, file handling)

📑 In This Lesson

What Are Superglobals?

In PHP, regular variables follow scope rules — a variable defined outside a function isn't visible inside it, and vice versa. Superglobals are the exception. They're built-in arrays that PHP populates automatically and that are accessible from any scope — global code, inside functions, inside class methods — without needing global declarations.

📖 Definition

Superglobal: A pre-defined associative array in PHP that is automatically available in all scopes throughout a script. PHP creates and populates these arrays before your script runs.

The Complete List of Superglobals

Superglobal Contains Lesson Coverage
$_SERVER Server and request information (paths, headers, method) This lesson (deep dive)
$_GET URL query string parameters Lesson 11 (review here)
$_POST Form data sent via POST method Lesson 11 (review here)
$_REQUEST Combined GET + POST + COOKIE data This lesson
$_FILES Uploaded file information This lesson (overview); Lesson 22 (full)
$_ENV Environment variables This lesson
$_COOKIE Cookies sent by the browser Preview here; Lesson 15 (full)
$_SESSION Session data (server-side) Preview here; Lesson 15 (full)
$GLOBALS All global variables This lesson

Why "Super"?

The "super" in superglobal means beyond normal global scope. Regular global variables require the global keyword to be accessed inside functions. Superglobals skip that step entirely:

<?php
$regularVar = "I'm a regular global variable";

function showRegular() {
    // echo $regularVar;  // Error! Not in scope
    global $regularVar;    // Must declare it global first
    echo $regularVar;      // Now it works
}

function showSuperglobal() {
    // No "global" declaration needed!
    echo $_SERVER["SERVER_NAME"];  // Just works
    echo $_GET["page"] ?? "home";  // Just works
}
flowchart TD REQ["Browser Request"] --> PHP["PHP Engine"] PHP --> SG["Populates Superglobals"] SG --> S["$_SERVER"] SG --> G["$_GET"] SG --> P["$_POST"] SG --> F["$_FILES"] SG --> C["$_COOKIE"] SG --> E["$_ENV"] SG --> SCRIPT["Your Script Runs"] S --> SCRIPT G --> SCRIPT P --> SCRIPT F --> SCRIPT C --> SCRIPT E --> SCRIPT style REQ fill:#dbeafe,stroke:#3b82f6 style SCRIPT fill:#dcfce7,stroke:#22c55e style SG fill:#fef3c7,stroke:#f59e0b

$_SERVER — Request & Server Info

$_SERVER is the richest superglobal. It contains information about the server environment, the current request, headers, paths, and the client. You've already used a few of its keys (like REQUEST_METHOD in Lesson 11). Let's explore the full picture.

Request Information

<?php
// Which HTTP method was used?
echo $_SERVER["REQUEST_METHOD"]; // "GET", "POST", "PUT", "DELETE", etc.

// What URL path was requested?
echo $_SERVER["REQUEST_URI"];    // "/products.php?id=42&sort=price"

// Just the script path (no query string)
echo $_SERVER["SCRIPT_NAME"];    // "/products.php"

// The query string alone
echo $_SERVER["QUERY_STRING"];   // "id=42&sort=price"

// The protocol
echo $_SERVER["SERVER_PROTOCOL"]; // "HTTP/1.1"

// Was it HTTPS?
$isSecure = (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off")
         || ($_SERVER["SERVER_PORT"] ?? 0) == 443;
echo $isSecure ? "Secure" : "Not secure";

Server Information

<?php
// The server's hostname
echo $_SERVER["SERVER_NAME"];    // "localhost" or "example.com"

// The port
echo $_SERVER["SERVER_PORT"];    // "80", "443", "8080", etc.

// Server software
echo $_SERVER["SERVER_SOFTWARE"]; // "Apache/2.4.58 (Ubuntu)"

// Document root (where web files live)
echo $_SERVER["DOCUMENT_ROOT"];  // "/var/www/html"

// Absolute filesystem path to this script
echo $_SERVER["SCRIPT_FILENAME"]; // "/var/www/html/products.php"

Client Information

<?php
// Client's IP address
echo $_SERVER["REMOTE_ADDR"];    // "192.168.1.100" or "::1" for localhost

// Client's port
echo $_SERVER["REMOTE_PORT"];    // "54321"

// User agent string (browser identification)
echo $_SERVER["HTTP_USER_AGENT"];
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..."

// Referring page (where the user came from)
echo $_SERVER["HTTP_REFERER"] ?? "Direct visit";
// "https://google.com/search?q=php+tutorials"

// Accepted languages
echo $_SERVER["HTTP_ACCEPT_LANGUAGE"];
// "en-US,en;q=0.9,es;q=0.8"

// Accepted content types
echo $_SERVER["HTTP_ACCEPT"];
// "text/html,application/xhtml+xml,..."

⚠️ Client Data Is User-Controlled

Values like HTTP_USER_AGENT, HTTP_REFERER, and HTTP_ACCEPT_LANGUAGE are sent by the browser and can be easily faked. Never trust them for security decisions. Always escape them before displaying them in HTML to prevent XSS attacks.

The Most Useful $_SERVER Keys

Key Example Value Common Use
REQUEST_METHOD "POST" Check if form was submitted
REQUEST_URI "/page?id=5" Full URL path with query string
SCRIPT_NAME "/page.php" Script path (no query string)
PHP_SELF "/page.php" Self-referencing forms (escape it!)
DOCUMENT_ROOT "/var/www/html" Building absolute file paths
REMOTE_ADDR "192.168.1.50" Logging, rate limiting
HTTP_USER_AGENT "Mozilla/5.0..." Analytics, browser detection
HTTP_REFERER "https://google.com" Traffic source tracking
SERVER_NAME "example.com" Building absolute URLs
REQUEST_TIME 1713400000 Timestamp when request started

Practical Example: Request Logger

<?php
function log_request(): void {
    $entry = [
        "time"    => date("Y-m-d H:i:s", $_SERVER["REQUEST_TIME"]),
        "method"  => $_SERVER["REQUEST_METHOD"],
        "uri"     => $_SERVER["REQUEST_URI"],
        "ip"      => $_SERVER["REMOTE_ADDR"],
        "agent"   => $_SERVER["HTTP_USER_AGENT"] ?? "Unknown",
        "referer" => $_SERVER["HTTP_REFERER"] ?? "Direct",
    ];

    $line = json_encode($entry) . "\n";
    file_put_contents(__DIR__ . "/logs/access.log", $line, FILE_APPEND | LOCK_EX);
}

// Call at the top of every page
log_request();

Practical Example: Simple URL Router

<?php
// A basic router using $_SERVER["REQUEST_URI"]
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$uri = rtrim($uri, "/") ?: "/"; // Normalize trailing slash

switch ($uri) {
    case "/":
        include "pages/home.php";
        break;
    case "/about":
        include "pages/about.php";
        break;
    case "/contact":
        include "pages/contact.php";
        break;
    default:
        http_response_code(404);
        include "pages/404.php";
        break;
}

Exploring $_SERVER — See Everything

A quick way to see all available $_SERVER values on your system:

<?php
// Create a debug page to see all $_SERVER values
echo "<h2>\$_SERVER Contents</h2>";
echo "<table border='1'>";
echo "<tr><th>Key</th><th>Value</th></tr>";
foreach ($_SERVER as $key => $value) {
    echo "<tr>";
    echo "<td>" . htmlspecialchars($key) . "</td>";
    echo "<td>" . htmlspecialchars($value) . "</td>";
    echo "</tr>";
}
echo "</table>";

✅ Try It Yourself

Create a file called server_info.php with the code above, then visit it in your browser. You'll see dozens of entries — some from Apache, some from PHP, some from the HTTP request. This is a great reference when you need to figure out which key holds the data you're looking for.

$_GET, $_POST & $_REQUEST

You already know $_GET and $_POST from Lesson 11. Let's review them briefly and introduce $_REQUEST.

Quick Review

<?php
// $_GET — data from the URL query string
// URL: /search.php?q=php+tutorials&page=2
echo $_GET["q"];     // "php tutorials"
echo $_GET["page"];  // "2"

// $_POST — data from form submissions with method="post"
// <form method="post"><input name="email">...
echo $_POST["email"]; // "user@example.com"

// Always provide defaults to avoid undefined key warnings
$search = $_GET["q"] ?? "";
$page   = (int)($_GET["page"] ?? 1);
$email  = $_POST["email"] ?? "";

$_REQUEST — The Combined Array

$_REQUEST merges data from $_GET, $_POST, and $_COOKIE into a single array. The merge order is controlled by the request_order PHP setting (default: "GP" — GET then POST, so POST overwrites GET on conflicts).

<?php
// URL: /page.php?source=google
// POST body: source=form_submit

echo $_GET["source"];     // "google"
echo $_POST["source"];    // "form_submit"
echo $_REQUEST["source"]; // "form_submit" (POST overwrites GET by default)

// $_REQUEST is convenient but ambiguous — you don't know where the data came from
// For clarity and security, prefer $_GET or $_POST directly

⚠️ Avoid $_REQUEST in Production

While $_REQUEST is convenient for quick prototyping, it mixes data sources and makes your code ambiguous. You can't tell whether a value came from the URL, a form submission, or a cookie. Stick to $_GET and $_POST for clarity and security.

Checking the Request Method

<?php
// The standard pattern for self-processing forms (from Lesson 11)
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // Form was submitted — process the data
    $name = trim($_POST["name"] ?? "");
    // ...validate and process...
}

// You can also handle multiple methods
$method = $_SERVER["REQUEST_METHOD"];
switch ($method) {
    case "GET":
        // Display the page / resource
        break;
    case "POST":
        // Create a new resource
        break;
    case "PUT":
        // Update an existing resource (API routes)
        break;
    case "DELETE":
        // Delete a resource (API routes)
        break;
}

$_FILES — Upload Data

When a form has enctype="multipart/form-data" and contains a file input, PHP populates $_FILES with information about each uploaded file. We'll cover file uploads in full detail in Lesson 22, but here's the essential overview.

The $_FILES Structure

<?php
// HTML: <input type="file" name="avatar">

// After upload, $_FILES["avatar"] contains:
$_FILES["avatar"] = [
    "name"     => "profile.jpg",     // Original filename from user's computer
    "type"     => "image/jpeg",      // MIME type (reported by browser — don't trust!)
    "tmp_name" => "/tmp/phpYX3a2b",  // Temporary path on server
    "error"    => 0,                 // Error code (0 = success)
    "size"     => 245890,            // Size in bytes
];

// For multiple files: <input type="file" name="photos[]" multiple>
// $_FILES["photos"]["name"][0], $_FILES["photos"]["name"][1], etc.

Upload Error Codes

Constant Value Meaning
UPLOAD_ERR_OK 0 Upload succeeded
UPLOAD_ERR_INI_SIZE 1 Exceeds upload_max_filesize in php.ini
UPLOAD_ERR_FORM_SIZE 2 Exceeds MAX_FILE_SIZE in form
UPLOAD_ERR_PARTIAL 3 Only partially uploaded
UPLOAD_ERR_NO_FILE 4 No file was uploaded
UPLOAD_ERR_NO_TMP_DIR 6 Missing temporary folder
UPLOAD_ERR_CANT_WRITE 7 Failed to write to disk

Basic Upload Check

<?php
// Quick check: was a file uploaded successfully?
if (isset($_FILES["avatar"]) && $_FILES["avatar"]["error"] === UPLOAD_ERR_OK) {
    $tmpPath  = $_FILES["avatar"]["tmp_name"];
    $origName = $_FILES["avatar"]["name"];
    $size     = $_FILES["avatar"]["size"];

    echo "Uploaded: $origName ($size bytes)\n";
    echo "Temp location: $tmpPath\n";

    // Move from temp to permanent location (covered fully in Lesson 22)
    // move_uploaded_file($tmpPath, "uploads/" . basename($origName));
} else {
    echo "No file uploaded or an error occurred.\n";
}

✅ Coming in Lesson 22

We'll build a complete file upload system with MIME type validation, size limits, safe filename generation, and proper error handling. For now, just understand the structure of $_FILES so you recognize it when you see it.

$_ENV — Environment Variables

Environment variables are key-value pairs set at the operating system or web server level. They're the standard way to configure applications without hardcoding sensitive values like database passwords, API keys, or feature flags into your source code.

Accessing Environment Variables

<?php
// Method 1: $_ENV superglobal
// NOTE: $_ENV may be empty depending on your php.ini "variables_order" setting
echo $_ENV["HOME"] ?? "Not set";     // "/home/www-data"
echo $_ENV["PATH"] ?? "Not set";     // "/usr/local/bin:/usr/bin:..."

// Method 2: getenv() — more reliable, works regardless of variables_order
echo getenv("HOME");                 // "/home/www-data"
echo getenv("DB_PASSWORD");          // "my_secret_password"

// getenv returns false if the variable doesn't exist
$apiKey = getenv("API_KEY");
if ($apiKey === false) {
    die("API_KEY environment variable is not set!");
}

// Method 3: $_SERVER may also contain environment variables
// Apache often passes env vars through to $_SERVER
echo $_SERVER["DB_HOST"] ?? "Not in \$_SERVER";

📖 Why $_ENV Might Be Empty

PHP's variables_order setting in php.ini controls which superglobals get populated. The default is "GPCS" (GET, POST, Cookie, Server) — notice "E" (Environment) is often missing! Use getenv() instead for reliable access, or add "E" to your variables_order.

Setting Environment Variables

<?php
// Set env var for the current script's lifetime
putenv("APP_MODE=development");
echo getenv("APP_MODE"); // "development"

// Common places to set env vars:
// 1. Apache config: SetEnv DB_HOST localhost
// 2. .htaccess: SetEnv APP_SECRET abc123
// 3. CLI: DB_HOST=localhost php script.php
// 4. Docker: ENV DB_HOST=localhost
// 5. .env files (with a library like vlucas/phpdotenv)

Practical Example: Configuration with Environment Variables

<?php
// config.php — load config from environment with safe defaults

$config = [
    "db" => [
        "host"     => getenv("DB_HOST") ?: "localhost",
        "name"     => getenv("DB_NAME") ?: "myapp",
        "user"     => getenv("DB_USER") ?: "root",
        "password" => getenv("DB_PASSWORD") ?: "",
        "port"     => (int)(getenv("DB_PORT") ?: 3306),
    ],
    "app" => [
        "name"    => getenv("APP_NAME") ?: "My App",
        "debug"   => getenv("APP_DEBUG") === "true",
        "url"     => getenv("APP_URL") ?: "http://localhost",
    ],
    "mail" => [
        "host"     => getenv("MAIL_HOST") ?: "smtp.mailtrap.io",
        "port"     => (int)(getenv("MAIL_PORT") ?: 587),
        "username" => getenv("MAIL_USER") ?: "",
        "password" => getenv("MAIL_PASS") ?: "",
    ],
];

// Now use $config throughout your app
// No passwords in source code!

✅ The Twelve-Factor App Rule

The Twelve-Factor App methodology says: store config in environment variables. This keeps secrets out of your code, lets the same codebase run in development, staging, and production, and makes deployment easier. Every modern framework (Laravel, Symfony) follows this pattern.

$_COOKIE & $_SESSION (Preview)

These two superglobals manage user state across requests. They're covered in full in Lesson 15: Sessions & Cookies, but here's a quick preview so you understand the landscape.

$_COOKIE — Browser-Stored Data

<?php
// Cookies are small pieces of data stored in the browser
// The browser sends them with every request to your domain

// Set a cookie (must happen before ANY output)
setcookie("username", "alice", time() + 3600); // Expires in 1 hour

// Read a cookie (available on the NEXT request, not the current one)
echo $_COOKIE["username"] ?? "No username cookie";

// $_COOKIE is populated from the Cookie: header in the HTTP request
// It only contains cookies that the browser sent — NOT cookies you
// just set with setcookie() on this same request

$_SESSION — Server-Stored Data

<?php
// Sessions store data on the server, linked to the user via a session ID cookie
session_start(); // Must call this before using $_SESSION

// Store data
$_SESSION["user_id"] = 42;
$_SESSION["username"] = "alice";
$_SESSION["role"] = "admin";

// Read data (persists across page loads)
echo $_SESSION["username"]; // "alice"

// Check if user is logged in
if (isset($_SESSION["user_id"])) {
    echo "Welcome back, " . htmlspecialchars($_SESSION["username"]);
} else {
    echo "Please log in.";
}

Cookie vs. Session — Quick Comparison

Feature $_COOKIE $_SESSION
Stored Where Browser (client) Server (file or database)
Size Limit ~4 KB per cookie Limited by server resources
Security User can read/modify User only has the session ID
Lifetime Set via expiration date Until session expires or is destroyed
Use Cases Preferences, "remember me" Login state, cart, user data
📍 Full Coverage in Lesson 15: We'll build a complete login/logout system with sessions, implement "remember me" with cookies, and cover all the security considerations (secure flags, httponly, session fixation, etc.).

$GLOBALS — The Master Array

$GLOBALS is a special superglobal that contains references to every variable in the global scope — including the other superglobals. It's the "master array" of all globally accessible variables.

<?php
$siteName = "PHP Foundations";
$version  = "1.0";

// Access global variables by name through $GLOBALS
echo $GLOBALS["siteName"]; // "PHP Foundations"
echo $GLOBALS["version"];  // "1.0"

// $GLOBALS contains ALL superglobals too
// $GLOBALS["_SERVER"] is the same as $_SERVER
// $GLOBALS["_GET"] is the same as $_GET
// (But you'd never access them this way — use the direct names)

// Modify a global variable from inside a function
function incrementCounter() {
    $GLOBALS["counter"]++;
    // Equivalent to:
    // global $counter;
    // $counter++;
}

$counter = 0;
incrementCounter();
incrementCounter();
echo $counter; // 2

⚠️ Avoid $GLOBALS in Modern PHP

$GLOBALS exists for historical reasons and edge cases, but using it creates hidden dependencies between functions and global state. Modern PHP favors dependency injection — passing data into functions as parameters rather than reaching into global state. We'll explore this pattern in the OOP lessons.

When $GLOBALS Is Actually Useful

<?php
// 1. Debugging — see all global variables at once
echo "<pre>";
print_r(array_keys($GLOBALS));
echo "</pre>";
// ["_GET", "_POST", "_COOKIE", "_FILES", "_SERVER", "_ENV",
//  "GLOBALS", "siteName", "version", "counter", ...]

// 2. Dynamic variable access (rare)
$varName = "siteName";
echo $GLOBALS[$varName]; // "PHP Foundations"

// 3. Library code that needs to register global state (legacy)
// Modern code: avoid this pattern entirely

Security Considerations

Superglobals carry user-supplied data — data from the URL, form submissions, cookies, and HTTP headers. All of this data is untrusted. Here are the key rules:

Rule 1: Never Trust, Always Validate

<?php
// BAD — using raw superglobal data
$id = $_GET["id"];           // Could be "1; DROP TABLE users"
$name = $_POST["name"];      // Could contain <script> tags

// GOOD — validate and sanitize
$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
    die("Invalid ID");
}

$name = trim($_POST["name"] ?? "");
$name = htmlspecialchars($name, ENT_QUOTES, "UTF-8");

Rule 2: Escape Output

<?php
// BAD — XSS vulnerability!
echo "Welcome, " . $_GET["name"];
// URL: ?name=<script>alert('hacked')</script>

// GOOD — escape for HTML context
echo "Welcome, " . htmlspecialchars($_GET["name"] ?? "", ENT_QUOTES, "UTF-8");

// Always use a helper function (from Lesson 13)
function e(string $text): string {
    return htmlspecialchars($text, ENT_QUOTES, "UTF-8");
}
echo "Welcome, " . e($_GET["name"] ?? "Guest");

Rule 3: Don't Expose $_SERVER to Users

<?php
// BAD — leaks server internals
echo "<p>Server: " . $_SERVER["SERVER_SOFTWARE"] . "</p>";
echo "<p>Path: " . $_SERVER["DOCUMENT_ROOT"] . "</p>";
phpinfo(); // NEVER leave this on a production server!

// GOOD — only expose what users need
// Keep phpinfo() files behind authentication or delete them after setup

Rule 4: Use PHP_SELF Safely

<?php
// BAD — XSS through PHP_SELF injection
echo '<form action="' . $_SERVER["PHP_SELF"] . '">';
// URL: /page.php/">

// GOOD — escape it, or use SCRIPT_NAME, or hardcode
echo '<form action="' . htmlspecialchars($_SERVER["PHP_SELF"]) . '">';
// or
echo '<form action="' . $_SERVER["SCRIPT_NAME"] . '">';
// or (simplest and safest)
echo '<form action="">'; // Empty action = submit to current URL

✅ The Golden Rule

Validate input. Escape output. Every value from a superglobal is user input (even $_SERVER keys like HTTP_USER_AGENT). Validate it before processing, escape it before displaying. This two-step approach prevents the vast majority of web vulnerabilities.

Hands-On Exercises

🏋️ Exercise 1: Server Info Dashboard

Objective: Build a diagnostic page that displays organized server and request information.

Instructions:

  1. Create server_dashboard.php
  2. Display sections for: Request Info, Server Info, Client Info, and PHP Info
  3. Under Request Info show: method, URI, query string, protocol, and whether HTTPS is active
  4. Under Server Info show: server name, port, software, document root, and PHP version (phpversion())
  5. Under Client Info show: IP address, user agent, referer, and accepted languages
  6. Under PHP Info show: memory_get_usage() (formatted in KB), PHP_OS, and php_sapi_name()
  7. Escape all output with htmlspecialchars()
💡 Hint

Use the e() helper function from Lesson 13 to escape output. Format memory with number_format(memory_get_usage() / 1024, 2) for KB. Use the null coalescing operator (??) for optional values like HTTP_REFERER.

✅ Solution
<?php
function e(string $text): string {
    return htmlspecialchars($text, ENT_QUOTES, "UTF-8");
}

$isSecure = (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off")
         || ($_SERVER["SERVER_PORT"] ?? 0) == 443;
?>
<!DOCTYPE html>
<html>
<head><title>Server Dashboard</title></head>
<body>
    <h1>Server Info Dashboard</h1>

    <h2>Request Info</h2>
    <table border="1">
        <tr><td>Method</td><td><?= e($_SERVER["REQUEST_METHOD"]) ?></td></tr>
        <tr><td>URI</td><td><?= e($_SERVER["REQUEST_URI"]) ?></td></tr>
        <tr><td>Query String</td><td><?= e($_SERVER["QUERY_STRING"] ?? "(none)") ?></td></tr>
        <tr><td>Protocol</td><td><?= e($_SERVER["SERVER_PROTOCOL"]) ?></td></tr>
        <tr><td>HTTPS</td><td><?= $isSecure ? "Yes" : "No" ?></td></tr>
    </table>

    <h2>Server Info</h2>
    <table border="1">
        <tr><td>Server Name</td><td><?= e($_SERVER["SERVER_NAME"]) ?></td></tr>
        <tr><td>Port</td><td><?= e($_SERVER["SERVER_PORT"]) ?></td></tr>
        <tr><td>Software</td><td><?= e($_SERVER["SERVER_SOFTWARE"]) ?></td></tr>
        <tr><td>Document Root</td><td><?= e($_SERVER["DOCUMENT_ROOT"]) ?></td></tr>
        <tr><td>PHP Version</td><td><?= e(phpversion()) ?></td></tr>
    </table>

    <h2>Client Info</h2>
    <table border="1">
        <tr><td>IP Address</td><td><?= e($_SERVER["REMOTE_ADDR"]) ?></td></tr>
        <tr><td>User Agent</td><td><?= e($_SERVER["HTTP_USER_AGENT"] ?? "Unknown") ?></td></tr>
        <tr><td>Referer</td><td><?= e($_SERVER["HTTP_REFERER"] ?? "Direct visit") ?></td></tr>
        <tr><td>Languages</td><td><?= e($_SERVER["HTTP_ACCEPT_LANGUAGE"] ?? "Not specified") ?></td></tr>
    </table>

    <h2>PHP Info</h2>
    <table border="1">
        <tr><td>Memory Usage</td><td><?= number_format(memory_get_usage() / 1024, 2) ?> KB</td></tr>
        <tr><td>OS</td><td><?= e(PHP_OS) ?></td></tr>
        <tr><td>SAPI</td><td><?= e(php_sapi_name()) ?></td></tr>
    </table>
</body>
</html>

🏋️ Exercise 2: Environment Config Loader

Objective: Build a simple .env file loader that reads key-value pairs into environment variables.

Instructions:

  1. Create a function load_env(string $path): void that reads a .env file
  2. Each line should be in the format KEY=value
  3. Skip blank lines and lines starting with # (comments)
  4. Trim whitespace from keys and values
  5. Use putenv() to set each variable
  6. Test it by creating a .env file and loading it

Sample .env file:

# Database settings
DB_HOST=localhost
DB_NAME=myapp
DB_USER=root
DB_PASSWORD=secret123

# App settings
APP_NAME=My Cool App
APP_DEBUG=true
APP_URL=http://localhost:8080
💡 Hint

Use file() with FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES to read the file. Use str_starts_with() to check for comments. Use explode("=", $line, 2) to split on the first = only (values can contain = characters).

✅ Solution
<?php
/**
 * Load a .env file and set environment variables.
 */
function load_env(string $path): void {
    if (!file_exists($path)) {
        throw new RuntimeException(".env file not found: $path");
    }

    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

    foreach ($lines as $line) {
        $line = trim($line);

        // Skip comments
        if (str_starts_with($line, "#")) {
            continue;
        }

        // Must contain an = sign
        if (!str_contains($line, "=")) {
            continue;
        }

        // Split on the FIRST = only (value might contain =)
        [$key, $value] = explode("=", $line, 2);
        $key   = trim($key);
        $value = trim($value);

        // Remove surrounding quotes if present
        if (
            (str_starts_with($value, '"') && str_ends_with($value, '"')) ||
            (str_starts_with($value, "'") && str_ends_with($value, "'"))
        ) {
            $value = substr($value, 1, -1);
        }

        // Set the environment variable
        putenv("$key=$value");
    }
}

// Usage
load_env(__DIR__ . "/.env");

echo getenv("DB_HOST");     // "localhost"
echo getenv("APP_NAME");    // "My Cool App"
echo getenv("APP_DEBUG");   // "true"

// Build a config array from environment
$config = [
    "db_host" => getenv("DB_HOST"),
    "db_name" => getenv("DB_NAME"),
    "debug"   => getenv("APP_DEBUG") === "true",
];
print_r($config);

🎯 Quick Quiz

Question 1: What makes superglobals different from regular global variables?

Question 2: Which $_SERVER key tells you what HTTP method was used for the request?

Question 3: Why should you avoid using $_REQUEST in production code?

Question 4: What's the most reliable way to read environment variables in PHP?

Question 5: Why is displaying raw $_SERVER["PHP_SELF"] in a form action a security risk?

Summary

🎉 Key Takeaways

  • Superglobals are built-in arrays available in all scopes without the global keyword
  • $_SERVER is the richest superglobal — request method, URI, headers, client IP, server paths, and more
  • $_GET holds URL query parameters; $_POST holds form data
  • $_REQUEST merges GET, POST, and COOKIE data — convenient but ambiguous; prefer the specific arrays
  • $_FILES holds uploaded file metadata (name, size, tmp path, error code)
  • $_ENV holds environment variables, but getenv() is more reliable
  • $_COOKIE holds browser-sent cookies; $_SESSION holds server-side session data (both covered in full in Lesson 15)
  • $GLOBALS contains all global variables — useful for debugging, but avoid in production code
  • Security: All superglobal data is user-controlled — validate input, escape output

📚 Additional Resources

🚀 What's Next?

Now that you know the full landscape of superglobals, it's time to put two of them to serious work. In Lesson 15: Sessions & Cookies, we'll build a complete login/logout system using $_SESSION and $_COOKIE, implement "remember me" functionality, and cover all the security best practices for managing user state.

🎉 Congratulations!

You now have a comprehensive understanding of PHP's superglobal system — the built-in data layer that powers every PHP web application. Onward to sessions and cookies!