🌐 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
$_SERVERto access request metadata, paths, and client information - Understand the relationship between
$_GET,$_POST, and$_REQUEST - Inspect
$_FILESfor uploaded file information - Access environment variables with
$_ENVandgetenv() - Preview
$_COOKIEand$_SESSION(covered in depth in Lesson 15) - Use
$GLOBALSto 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
}
$_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:
- Create
server_dashboard.php - Display sections for: Request Info, Server Info, Client Info, and PHP Info
- Under Request Info show: method, URI, query string, protocol, and whether HTTPS is active
- Under Server Info show: server name, port, software, document root, and PHP version (
phpversion()) - Under Client Info show: IP address, user agent, referer, and accepted languages
- Under PHP Info show:
memory_get_usage()(formatted in KB),PHP_OS, andphp_sapi_name() - 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:
- Create a function
load_env(string $path): voidthat reads a.envfile - Each line should be in the format
KEY=value - Skip blank lines and lines starting with
#(comments) - Trim whitespace from keys and values
- Use
putenv()to set each variable - Test it by creating a
.envfile 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
globalkeyword $_SERVERis the richest superglobal — request method, URI, headers, client IP, server paths, and more$_GETholds URL query parameters;$_POSTholds form data$_REQUESTmerges GET, POST, and COOKIE data — convenient but ambiguous; prefer the specific arrays$_FILESholds uploaded file metadata (name, size, tmp path, error code)$_ENVholds environment variables, butgetenv()is more reliable$_COOKIEholds browser-sent cookies;$_SESSIONholds server-side session data (both covered in full in Lesson 15)$GLOBALScontains all global variables — useful for debugging, but avoid in production code- Security: All superglobal data is user-controlled — validate input, escape output
📚 Additional Resources
- PHP Manual: Superglobals
- PHP Manual: $_SERVER
- PHP Manual: $_FILES
- PHP Manual: getenv()
- The Twelve-Factor App: Config
🚀 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!