1. Arrays: Indexed, Associative & Multidimensional
PHP has one array type that covers what other languages split into lists, dictionaries and tuples. An indexed array uses sequential integer keys; an associative array uses string keys you choose:
<?php
declare(strict_types=1);
// Indexed
$colors = ["red", "green", "blue"];
echo $colors[1]; // "green"
// Associative
$product = [
"name" => "Mechanical Keyboard",
"price" => 89.99,
"inStock" => true,
];
echo $product["name"]; // "Mechanical Keyboard"
// Multidimensional -- an array of associative arrays
$cart = [
["name" => "Keyboard", "qty" => 1],
["name" => "Mouse", "qty" => 2],
];
echo $cart[1]["name"]; // "Mouse"
Under the hood, PHP arrays are ordered maps — even a "list" with
integer keys is really a map from 0, 1, 2, ... to values, which is
why associative and indexed arrays are the same underlying type. Iterate any array
with foreach:
<?php
foreach ($cart as $index => $item) {
echo "{$index}: {$item['name']} x{$item['qty']}\n";
}
// Common array functions
$names = array_column($cart, 'name'); // ["Keyboard", "Mouse"]
$totalQty = array_sum(array_column($cart, 'qty')); // 3
$inStock = array_filter([$product], fn($p) => $p['inStock']);
$prices = array_map(fn($p) => $p['price'] * 1.18, [$product]); // with tax
array_map, array_filter & array_reduce
These three cover most transformations you'll reach for: array_map transforms every element, array_filter keeps only elements matching a predicate, and array_reduce folds the array down to a single value. Reaching for these instead of hand-rolled loops keeps intent obvious at a glance.
2. String Functions & Formatting
PHP's string library is large and mostly flat functions rather than methods —
strtoupper($s), not $s.toUpperCase(). The ones you'll use
constantly:
<?php
$title = " Mechanical Keyboard ";
trim($title); // "Mechanical Keyboard"
strtolower($title); // " mechanical keyboard "
str_contains($title, "Keyboard"); // true
str_replace("Keyboard", "Mouse", $title); // " Mechanical Mouse "
substr($title, 2, 10); // "Mechanical"
strlen(trim($title)); // 19
explode(" ", trim($title)); // ["Mechanical", "Keyboard"]
implode("-", ["a", "b", "c"]); // "a-b-c"
// Interpolation -- variables inside double-quoted strings are expanded;
// braces are required for array/property access inside the string
$qty = 3;
echo "You have {$qty} items in your cart.";
// sprintf for precise formatting
sprintf("Total: $%.2f", 89.99 * 1.18); // "Total: $106.19"
Single-quoted strings 'like this' never interpolate variables or
escape sequences (except \' and \\) — use them for
literal text and double-quoted strings when you need interpolation, so the
distinction itself signals intent.
3. Superglobals
PHP automatically populates several arrays with request data — they're called
superglobals because they're available in every scope without
needing to be passed in or declared global:
$_GET— query string parameters (?id=42)$_POST— form body data from a POST request$_SERVER— request metadata: method, URI, headers, IP$_SESSION— per-visitor data persisted across requests (Week 5)$_FILES— uploaded file metadata (Week 5)
<?php declare(strict_types=1);
// GET /search.php?q=keyboard&page=2
$query = $_GET['q'] ?? '';
$page = (int) ($_GET['page'] ?? 1);
// Request metadata
$method = $_SERVER['REQUEST_METHOD']; // "GET"
$path = $_SERVER['REQUEST_URI']; // "/search.php?q=keyboard&page=2"
$ip = $_SERVER['REMOTE_ADDR'];
The ?? null coalescing operator is the idiomatic way to read
superglobal keys that might not be set — $_GET['q'] ?? '' reads as
"the q parameter, or an empty string if it's missing," avoiding an
"undefined array key" warning.
4. Building an HTML Form Handler
A form's method attribute determines which superglobal receives its
data. Here's a complete contact form and the script that handles it — both in one
file, branching on the request method:
<?php
declare(strict_types=1);
$errors = [];
$submitted = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
if ($name === '') {
$errors[] = 'Name is required.';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'A valid email address is required.';
}
if (strlen($message) < 10) {
$errors[] = 'Message must be at least 10 characters.';
}
if (empty($errors)) {
$submitted = true;
// In Week 7 this becomes an INSERT into MySQL instead.
}
}
?>
<!DOCTYPE html>
<html>
<body>
<?php if ($submitted): ?>
<p>Thanks, <?= htmlspecialchars($name) ?>! We'll be in touch.</p>
<?php else: ?>
<?php foreach ($errors as $error): ?>
<p class="error"><?= htmlspecialchars($error) ?></p>
<?php endforeach; ?>
<form method="post" action="contact.php">
<input name="name" placeholder="Name" value="<?= htmlspecialchars($_POST['name'] ?? '') ?>">
<input name="email" placeholder="Email" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">
<textarea name="message"><?= htmlspecialchars($_POST['message'] ?? '') ?></textarea>
<button type="submit">Send</button>
</form>
<?php endif; ?>
</body>
</html>
Notice the form re-fills each field's value from $_POST
on a failed submission — a small detail that makes validation errors far less
annoying for the person filling out the form.
5. Sanitizing & Validating Input
PHP's filter_var() function validates and sanitizes common data
shapes without writing manual regex for every case:
<?php
filter_var("ada@example.com", FILTER_VALIDATE_EMAIL); // "ada@example.com"
filter_var("not-an-email", FILTER_VALIDATE_EMAIL); // false
filter_var("42", FILTER_VALIDATE_INT); // 42
filter_var("https://example.com", FILTER_VALIDATE_URL); // the URL, or false
Validating rejects bad input. Sanitizing strips or transforms it into a safer shape. Escaping (like htmlspecialchars()) makes it safe for a specific output context — HTML here, but SQL and shell commands each need their own escaping mechanism, covered in Weeks 7 and 8. All three matter, and none substitutes for the others.
6. Hands-on Exercise
Build a validated newsletter signup form
Apply this week's arrays, strings and superglobals to a complete form-handling script.
Requirements:
- Create
signup.phpwith a form (name,email, and a<select>forinterestwith at least 3 options) that POSTs to itself. - On submit, validate:
namenon-empty,emailpassesFILTER_VALIDATE_EMAIL,interestis one of the allowed options (check within_array). - Collect errors into an indexed array and display each one if validation fails, re-filling the form's previous values.
- On success, append the validated data as an associative array to a
$signupsarray (in-memory is fine — persistence arrives in Week 7), and display a confirmation listing every field back usinghtmlspecialchars(). - Add a summary line below the form using
sprintf()that reads like"3 people have signed up so far."
Since PHP re-runs the script from scratch on every request, an in-memory $signups array won't persist between submissions yet — that's expected. Focus on getting the validation, re-population and array handling right; real persistence is Week 7's job.
7. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What's the actual difference between an "indexed" and an "associative" PHP array?
What's the actual difference between an "indexed" and an "associative" PHP array?
Nothing at the type level — PHP has a single array type, which is really an ordered map from keys to values. An "indexed" array is simply one whose keys happen to be sequential integers starting at 0; an "associative" array uses string (or non-sequential integer) keys you choose. Both are the exact same underlying structure.
Q2
Why use $_GET['q'] ?? '' instead of just $_GET['q']?
Why use $_GET['q'] ?? '' instead of just $_GET['q']?
If q wasn't in the query string, $_GET['q'] raises an "undefined array key" warning and evaluates to null. The ?? null coalescing operator returns the right-hand fallback whenever the left side is unset or null, so $_GET['q'] ?? '' safely reads "the value, or an empty string" in one expression with no warning.
Q3
Does a form with method="get" populate $_POST?
Does a form with method="get" populate $_POST?
No — a GET form appends its fields to the URL's query string, which lands in $_GET, not $_POST. Only a form with method="post" sends its fields in the request body, which PHP parses into $_POST. Checking $_SERVER['REQUEST_METHOD'] is how the handler script tells which case it's in.
Q4
If filter_var($email, FILTER_VALIDATE_EMAIL) confirms an address is well-formed, is it now safe to print directly into HTML?
If filter_var($email, FILTER_VALIDATE_EMAIL) confirms an address is well-formed, is it now safe to print directly into HTML?
No — validation and output escaping are separate concerns. Email validation only confirms the string is shaped like an address; it doesn't guarantee the value is free of characters that are dangerous in an HTML context. Always pass user-controlled values through htmlspecialchars() (or the right escaping function for the output context) regardless of what validation already ran.