Skip to main content

šŸ“¦ Lesson 8: Arrays

Arrays are PHP's Swiss Army knife — a single variable that holds an entire collection of values. Whether you're building a shopping cart, storing database rows, or processing form data, arrays are at the center of it all.

šŸŽÆ Learning Objectives

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

  • Create and manipulate indexed (numeric) arrays
  • Build and access associative (key-value) arrays
  • Work with multidimensional arrays (arrays of arrays)
  • Add, remove, and modify array elements
  • Use array destructuring to unpack values cleanly
  • Combine and expand arrays with the spread operator (...)
  • Loop through arrays with foreach and other techniques

Estimated Time: 50 minutes

Prerequisites: Lesson 7 (functions, callbacks, arrow functions)

šŸ“‘ In This Lesson

What Are Arrays?

šŸ“– Definition

Array: An ordered collection of values stored in a single variable. Each value (called an element) is identified by a key — either a number (indexed array) or a string (associative array).

Without arrays, storing a list of five student names would require five separate variables. With an array, you store them all in one place and access them by position or label.

Without Arrays With an Array
$student1 = "Alice";
$student2 = "Bob";
$student3 = "Carol";
$students = ["Alice", "Bob", "Carol"];
Hard to loop, hard to pass around Easy to loop, easy to pass to functions

PHP has two flavors of array (but they're really the same data structure under the hood):

flowchart TD A["PHP Array"] --> B["Indexed Array
Keys are integers: 0, 1, 2, ..."] A --> C["Associative Array
Keys are strings: 'name', 'age', ..."] B --> D["$colors = ['red', 'blue', 'green']"] C --> E["$user = ['name' => 'Alice', 'age' => 30]"]
šŸ’” Key Insight: In PHP, all arrays are actually ordered maps — they map keys to values. An "indexed" array is just an array where the keys happen to be consecutive integers starting at 0. This is different from languages like Java or C where arrays and maps are separate data structures.

Indexed Arrays

An indexed array uses integer keys, starting at 0 by default.

Creating Indexed Arrays

<?php
// Short syntax (preferred since PHP 5.4)
$fruits = ["apple", "banana", "cherry"];

// Legacy syntax (you'll see this in older code)
$fruits = array("apple", "banana", "cherry");

// Empty array — fill it later
$colors = [];

// Creating with specific values
$primes = [2, 3, 5, 7, 11, 13];
$mixed  = ["hello", 42, true, 3.14, null]; // PHP arrays can hold mixed types

Accessing Elements by Index

Array indices start at 0, not 1:

<?php
$fruits = ["apple", "banana", "cherry", "date", "elderberry"];
//          [0]       [1]       [2]       [3]     [4]

echo $fruits[0]; // apple    (first element)
echo $fruits[2]; // cherry   (third element)
echo $fruits[4]; // elderberry (last element)

// Negative indices? PHP doesn't support them natively.
// But you can calculate from the end:
$last = $fruits[count($fruits) - 1]; // elderberry

// Access a non-existent index
echo $fruits[10]; // āš ļø Warning: Undefined array key 10

āš ļø Off-by-One Errors

The most common array bug: an array with 5 elements has indices 0 through 4, not 1 through 5. If you try $fruits[5], you'll get an undefined key warning.

Counting Elements

<?php
$colors = ["red", "green", "blue"];

echo count($colors);   // 3
echo sizeof($colors);  // 3 (alias for count — same function)

// Empty check
if (count($colors) === 0) {
    echo "No colors!\n";
}

// Or use the more readable empty() function
if (empty($colors)) {
    echo "No colors!\n";
}

Printing Arrays for Debugging

<?php
$scores = [95, 87, 72, 100, 68];

// print_r — human-readable
print_r($scores);
/* Output:
Array
(
    [0] => 95
    [1] => 87
    [2] => 72
    [3] => 100
    [4] => 68
)
*/

// var_dump — detailed (with types and sizes)
var_dump($scores);
/* Output:
array(5) {
  [0] => int(95)
  [1] => int(87)
  [2] => int(72)
  [3] => int(100)
  [4] => int(68)
}
*/

// Quick one-line debug trick
echo "<pre>" . print_r($scores, true) . "</pre>";

āœ… Pro Tip: print_r with true

Passing true as the second argument to print_r() makes it return the string instead of printing it. This is useful for logging, wrapping in <pre> tags, or storing the output.

Associative Arrays

An associative array uses string keys (sometimes called named keys) instead of numbers. Think of them as dictionaries, maps, or objects from other languages.

Creating Associative Arrays

<?php
$user = [
    "name"  => "Alice",
    "email" => "alice@example.com",
    "age"   => 30,
    "admin" => false,
];

// The => operator means "maps to"
// "name" => "Alice" means: key "name" maps to value "Alice"

Accessing Values by Key

<?php
$user = [
    "name"  => "Alice",
    "email" => "alice@example.com",
    "age"   => 30,
];

echo $user["name"];   // Alice
echo $user["email"];  // alice@example.com
echo $user["age"];    // 30

// Variable interpolation in strings
echo "Hello, {$user['name']}! You are {$user['age']} years old.\n";

// Non-existent key
echo $user["phone"]; // āš ļø Warning: Undefined array key "phone"

Safe Access with Null Coalescing

Use ?? to provide a default value for missing keys:

<?php
$user = ["name" => "Alice", "email" => "alice@example.com"];

// If key exists, use it; otherwise use the default
$phone = $user["phone"] ?? "Not provided";
echo $phone; // "Not provided"

$name = $user["name"] ?? "Unknown";
echo $name;  // "Alice" (key exists, so default is ignored)

When to Use Indexed vs. Associative

Use Case Type Example
List of similar items Indexed $colors = ["red", "blue", "green"];
A single record with fields Associative $user = ["name" => "Alice", "age" => 30];
Database rows Indexed of associative $users = [["name" => "Alice"], ...];
Config settings Associative $config = ["debug" => true, "db" => "mysql"];
Lookup / dictionary Associative $codes = ["US" => "United States", ...];

Modifying Arrays

Adding Elements

<?php
// --- Indexed Arrays ---
$fruits = ["apple", "banana"];

// Append to end (most common)
$fruits[] = "cherry";     // $fruits is now ["apple", "banana", "cherry"]
$fruits[] = "date";       // $fruits is now ["apple", "banana", "cherry", "date"]

// array_push — same as [], but can add multiple
array_push($fruits, "elderberry", "fig");
// $fruits is now ["apple", "banana", "cherry", "date", "elderberry", "fig"]

// Add to beginning
array_unshift($fruits, "avocado");
// ["avocado", "apple", "banana", "cherry", "date", "elderberry", "fig"]

// Insert at specific position (index 2)
array_splice($fruits, 2, 0, ["blueberry"]);
// ["avocado", "apple", "blueberry", "banana", ...]

// --- Associative Arrays ---
$user = ["name" => "Alice"];

// Add a new key
$user["email"] = "alice@example.com";
$user["age"] = 30;
// ["name" => "Alice", "email" => "alice@example.com", "age" => 30]

Updating Elements

<?php
$fruits = ["apple", "banana", "cherry"];

// Update by index
$fruits[1] = "blueberry";
// ["apple", "blueberry", "cherry"]

$user = ["name" => "Alice", "age" => 30];

// Update by key
$user["age"] = 31;
$user["name"] = "Alice Johnson";
// ["name" => "Alice Johnson", "age" => 31]

Removing Elements

<?php
$fruits = ["apple", "banana", "cherry", "date"];

// Remove last element
$last = array_pop($fruits);
echo $last; // "date"
// $fruits is now ["apple", "banana", "cherry"]

// Remove first element
$first = array_shift($fruits);
echo $first; // "apple"
// $fruits is now ["banana", "cherry"]

// Remove by index (leaves a gap!)
$colors = ["red", "green", "blue", "yellow"];
unset($colors[1]); // Removes "green"
print_r($colors);
// Array ( [0] => red [2] => blue [3] => yellow )
// Notice: index 1 is GONE, but 2 and 3 didn't shift down!

// Re-index after unset
$colors = array_values($colors);
// Array ( [0] => red [1] => blue [2] => yellow )

// Remove from associative array
$user = ["name" => "Alice", "email" => "alice@example.com", "temp" => "delete me"];
unset($user["temp"]);
// ["name" => "Alice", "email" => "alice@example.com"]

āš ļø unset() Leaves Gaps in Indexed Arrays

When you unset() an element from an indexed array, the remaining keys don't renumber themselves. Use array_values() to re-index, or use array_splice() instead, which automatically re-indexes.

// array_splice removes AND re-indexes
$colors = ["red", "green", "blue", "yellow"];
array_splice($colors, 1, 1); // Remove 1 element at index 1
// ["red", "blue", "yellow"] — clean sequential indices!

Checking If Keys or Values Exist

<?php
$user = ["name" => "Alice", "email" => "alice@example.com", "active" => false];

// Does a KEY exist?
echo array_key_exists("name", $user);     // true
echo array_key_exists("phone", $user);    // false

// isset() — checks key exists AND value is not null
echo isset($user["name"]);   // true
echo isset($user["phone"]);  // false

// The difference: isset vs array_key_exists
$data = ["value" => null];
echo array_key_exists("value", $data); // true  — key exists
echo isset($data["value"]);           // false — key exists but value is null!

// Does a VALUE exist in the array?
$fruits = ["apple", "banana", "cherry"];
echo in_array("banana", $fruits);   // true
echo in_array("grape", $fruits);    // false

// Strict comparison (recommended)
echo in_array("1", [1, 2, 3]);         // true  — "1" == 1 (loose)
echo in_array("1", [1, 2, 3], true);   // false — "1" !== 1 (strict)

Looping Through Arrays

foreach — The Go-To Loop for Arrays

The foreach loop is designed specifically for arrays. It visits every element without needing to manage an index:

<?php
// Indexed array — value only
$fruits = ["apple", "banana", "cherry"];

foreach ($fruits as $fruit) {
    echo "I like $fruit\n";
}
// I like apple
// I like banana
// I like cherry

// Indexed array — index and value
foreach ($fruits as $index => $fruit) {
    echo "$index: $fruit\n";
}
// 0: apple
// 1: banana
// 2: cherry

// Associative array — key and value
$user = ["name" => "Alice", "age" => 30, "city" => "Portland"];

foreach ($user as $key => $value) {
    echo "$key: $value\n";
}
// name: Alice
// age: 30
// city: Portland

Modifying Values During Iteration

To change array elements inside a foreach, use a reference (&):

<?php
$prices = [10.99, 24.50, 7.25, 18.00];

// Apply 10% discount to all prices
foreach ($prices as &$price) {
    $price = round($price * 0.90, 2);
}
unset($price); // ← ALWAYS unset the reference after the loop!

print_r($prices);
// [9.89, 22.05, 6.53, 16.2]

āš ļø Always unset() After Reference Loops

If you use foreach ($array as &$item), the $item variable remains a reference to the last element after the loop ends. If you later use $item for something else, you'll accidentally overwrite that last element. Always unset($item) after the loop.

for Loop with Arrays

Sometimes a traditional for loop is useful when you need the index for calculations:

<?php
$scores = [85, 92, 78, 95, 88];

for ($i = 0; $i < count($scores); $i++) {
    echo "Student " . ($i + 1) . ": {$scores[$i]}\n";
}

// Pro tip: cache count() for performance in tight loops
$len = count($scores);
for ($i = 0; $i < $len; $i++) {
    // ...
}

Building HTML with foreach

A very common real-world use — generating HTML from array data:

<?php
$nav_items = [
    ["label" => "Home",    "url" => "/"],
    ["label" => "About",   "url" => "/about"],
    ["label" => "Contact", "url" => "/contact"],
];
?>

<nav>
    <ul>
        <?php foreach ($nav_items as $item): ?>
            <li><a href="<?= $item['url'] ?>"><?= $item['label'] ?></a></li>
        <?php endforeach; ?>
    </ul>
</nav>

āœ… Alternative Syntax for Templates

When mixing PHP with HTML, use the alternative syntax: foreach (...): ... endforeach;. It's easier to read than curly braces when HTML sits between the opening and closing of the loop.

Multidimensional Arrays

A multidimensional array is an array of arrays. They're how you represent tables, records from a database, nested data structures, and more.

2D Array — Array of Indexed Arrays

<?php
// A grid / matrix
$matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

echo $matrix[0][0]; // 1  (row 0, column 0)
echo $matrix[1][2]; // 6  (row 1, column 2)
echo $matrix[2][1]; // 8  (row 2, column 1)

Array of Associative Arrays (Most Common!)

This is by far the most common pattern — it represents a list of records, like rows from a database:

<?php
$employees = [
    ["name" => "Alice",  "dept" => "Engineering", "salary" => 95000],
    ["name" => "Bob",    "dept" => "Marketing",   "salary" => 72000],
    ["name" => "Carol",  "dept" => "Engineering", "salary" => 105000],
    ["name" => "Dave",   "dept" => "Marketing",   "salary" => 68000],
];

// Access a specific record
echo $employees[0]["name"];    // Alice
echo $employees[2]["salary"];  // 105000

// Loop through records
foreach ($employees as $emp) {
    echo "{$emp['name']} works in {$emp['dept']} and earns \${$emp['salary']}\n";
}

// Find engineers only
foreach ($employees as $emp) {
    if ($emp["dept"] === "Engineering") {
        echo "šŸ”§ {$emp['name']}\n";
    }
}

Nested Associative Arrays

<?php
$config = [
    "app" => [
        "name"    => "My PHP App",
        "version" => "2.1.0",
        "debug"   => true,
    ],
    "database" => [
        "host"     => "localhost",
        "name"     => "myapp_db",
        "user"     => "root",
        "password" => "",
    ],
    "mail" => [
        "driver" => "smtp",
        "host"   => "smtp.example.com",
        "port"   => 587,
    ],
];

// Drill into nested levels
echo $config["app"]["name"];       // My PHP App
echo $config["database"]["host"];  // localhost
echo $config["mail"]["port"];      // 587

// Safe deep access with null coalescing
$cacheDriver = $config["cache"]["driver"] ?? "file";
// "file" — the "cache" key doesn't exist, so we get the default
flowchart TD A["$config"] --> B["'app'"] A --> C["'database'"] A --> D["'mail'"] B --> B1["'name' => 'My PHP App'"] B --> B2["'version' => '2.1.0'"] B --> B3["'debug' => true"] C --> C1["'host' => 'localhost'"] C --> C2["'name' => 'myapp_db'"] C --> C3["'user' => 'root'"] D --> D1["'driver' => 'smtp'"] D --> D2["'host' => 'smtp.example.com'"] D --> D3["'port' => 587"]

Looping Nested Arrays

<?php
$departments = [
    "Engineering" => ["Alice", "Carol", "Eve"],
    "Marketing"   => ["Bob", "Dave"],
    "Design"      => ["Frank", "Grace", "Heidi"],
];

foreach ($departments as $dept => $members) {
    echo "\nšŸ“ $dept (" . count($members) . " people):\n";
    foreach ($members as $index => $name) {
        echo "  " . ($index + 1) . ". $name\n";
    }
}
/* Output:
šŸ“ Engineering (3 people):
  1. Alice
  2. Carol
  3. Eve

šŸ“ Marketing (2 people):
  1. Bob
  2. Dave

šŸ“ Design (3 people):
  1. Frank
  2. Grace
  3. Heidi
*/

Array Destructuring

Destructuring lets you unpack array values into individual variables in one statement. It's a clean alternative to accessing elements one by one.

Indexed Array Destructuring

<?php
$coordinates = [37.7749, -122.4194];

// Without destructuring
$lat = $coordinates[0];
$lng = $coordinates[1];

// With destructuring (short syntax — PHP 7.1+)
[$lat, $lng] = $coordinates;
echo "Lat: $lat, Lng: $lng\n"; // Lat: 37.7749, Lng: -122.4194

// Legacy syntax (still works)
list($lat, $lng) = $coordinates;

// Skip elements with empty positions
$rgb = [255, 128, 0];
[, , $blue] = $rgb;  // Only grab the third value
echo $blue; // 0

[$red, , $blue] = $rgb; // Grab first and third
echo "$red, $blue"; // 255, 0

Associative Array Destructuring

<?php
$user = ["name" => "Alice", "email" => "alice@example.com", "age" => 30];

// Destructure by key name
["name" => $name, "email" => $email] = $user;
echo "$name ($email)\n"; // Alice (alice@example.com)

// You don't have to grab every key — just the ones you need
["age" => $age] = $user;
echo "Age: $age\n"; // Age: 30

// Rename variables during destructuring
["name" => $userName, "email" => $userEmail] = $user;
echo $userName; // Alice

Destructuring in foreach

This is where destructuring really shines — clean access to fields inside a loop:

<?php
$products = [
    ["name" => "Widget",    "price" => 12.99, "qty" => 50],
    ["name" => "Gadget",    "price" => 49.99, "qty" => 12],
    ["name" => "Doohickey", "price" => 8.50,  "qty" => 200],
];

// Without destructuring
foreach ($products as $product) {
    echo "{$product['name']}: \${$product['price']} ({$product['qty']} in stock)\n";
}

// With destructuring — much cleaner!
foreach ($products as ["name" => $name, "price" => $price, "qty" => $qty]) {
    echo "$name: \$$price ($qty in stock)\n";
}

// Indexed arrays in foreach
$points = [[1, 2], [3, 4], [5, 6]];

foreach ($points as [$x, $y]) {
    echo "Point ($x, $y)\n";
}

Destructuring Function Return Values

<?php
function get_min_max(array $numbers): array {
    return ["min" => min($numbers), "max" => max($numbers)];
}

// Destructure the return value directly
["min" => $min, "max" => $max] = get_min_max([4, 8, 1, 15, 3]);
echo "Min: $min, Max: $max"; // Min: 1, Max: 15

āœ… When to Use Destructuring

Destructuring is ideal when you need to work with specific fields from an array — especially inside loops. It gives each value a meaningful variable name, making code more readable than $row['field'] repeated everywhere.

The Spread Operator (...)

The spread operator (...) "unpacks" an array into individual values. It's the inverse of gathering — instead of collecting values into an array, it expands an array into separate values.

Merging Arrays

<?php
$fruits = ["apple", "banana"];
$veggies = ["carrot", "spinach"];

// Spread to merge
$food = [...$fruits, ...$veggies];
// ["apple", "banana", "carrot", "spinach"]

// Add items while merging
$food = [...$fruits, "cherry", ...$veggies, "kale"];
// ["apple", "banana", "cherry", "carrot", "spinach", "kale"]

// Compare with array_merge
$food = array_merge($fruits, $veggies); // Same result for indexed arrays

Spread with Associative Arrays (PHP 8.1+)

<?php
$defaults = [
    "theme"    => "light",
    "language" => "en",
    "timezone" => "UTC",
];

$userPrefs = [
    "theme"    => "dark",
    "language" => "es",
];

// Later values override earlier ones (like array_merge)
$settings = [...$defaults, ...$userPrefs];
// ["theme" => "dark", "language" => "es", "timezone" => "UTC"]

// Override specific values inline
$settings = [...$defaults, "theme" => "dark"];
// ["theme" => "dark", "language" => "en", "timezone" => "UTC"]

āœ… Spread for Config/Defaults Pattern

The spread operator is perfect for merging default settings with user overrides. Put defaults first, then spread user preferences — later values win.

Spread in Function Calls

You can unpack an array into function arguments:

<?php
function create_user(string $name, string $email, string $role): void {
    echo "Created $name ($email) as $role\n";
}

$userData = ["Alice", "alice@example.com", "admin"];
create_user(...$userData);
// Created Alice (alice@example.com) as admin

// With named keys (must match parameter names)
$userData = ["name" => "Bob", "role" => "editor", "email" => "bob@example.com"];
create_user(...$userData);
// Created Bob (bob@example.com) as editor — order doesn't matter with named keys!

Copying Arrays

<?php
$original = [1, 2, 3, 4, 5];

// Spread creates a shallow copy
$copy = [...$original];

$copy[] = 6;
print_r($original); // [1, 2, 3, 4, 5] — original unchanged
print_r($copy);     // [1, 2, 3, 4, 5, 6]

Useful Array Checks

A quick reference of functions you'll use constantly when working with arrays:

Function Purpose Example
count($arr) Number of elements count([1,2,3]) → 3
empty($arr) Is it empty? empty([]) → true
is_array($val) Is it an array? is_array("hi") → false
in_array($val, $arr) Does value exist? in_array("b", ["a","b"]) → true
array_key_exists($k, $arr) Does key exist? array_key_exists("name", $user)
array_keys($arr) Get all keys array_keys($user) → ["name","age"]
array_values($arr) Get all values (re-index) array_values($user) → ["Alice",30]
array_search($val, $arr) Find key of a value array_search("b", ["a","b"]) → 1
<?php
$fruits = ["apple", "banana", "cherry"];

// Get all keys
print_r(array_keys($fruits)); // [0, 1, 2]

// Find a value's position
$pos = array_search("banana", $fruits);
echo $pos; // 1

// array_search returns false if not found (use strict check!)
$pos = array_search("grape", $fruits);
if ($pos === false) {
    echo "Not found!\n";
}

// Check if something is actually an array
function process_items($items) {
    if (!is_array($items)) {
        echo "Expected an array!\n";
        return;
    }
    // ... process items
}
šŸ’” Tip: Always use strict comparison (===) when checking the result of array_search(). It returns 0 when the item is at index 0, and 0 is falsy! Using == false would incorrectly report "not found" for the first element.

Hands-On Exercises

šŸ‹ļø Exercise 1: Student Gradebook

Objective: Build and query an associative array of student records.

Instructions:

  1. Create an array of 5 students, each with: name, grade (A–F), and score (0–100)
  2. Write a function get_average(array $students): float that returns the average score
  3. Write a function get_highest(array $students): array that returns the student with the highest score
  4. Write a function get_by_grade(array $students, string $grade): array that returns all students with a given grade
  5. Display a formatted report showing each student, the average, and the top student
šŸ’” Hint

For get_highest, loop through and track the current highest score and corresponding student. For get_by_grade, build a new array of matches using foreach.

āœ… Solution
<?php
$students = [
    ["name" => "Alice",  "grade" => "A", "score" => 95],
    ["name" => "Bob",    "grade" => "B", "score" => 84],
    ["name" => "Carol",  "grade" => "A", "score" => 92],
    ["name" => "Dave",   "grade" => "C", "score" => 73],
    ["name" => "Eve",    "grade" => "B", "score" => 88],
];

function get_average(array $students): float {
    $total = 0;
    foreach ($students as $student) {
        $total += $student["score"];
    }
    return $total / count($students);
}

function get_highest(array $students): array {
    $top = $students[0];
    foreach ($students as $student) {
        if ($student["score"] > $top["score"]) {
            $top = $student;
        }
    }
    return $top;
}

function get_by_grade(array $students, string $grade): array {
    $matches = [];
    foreach ($students as $student) {
        if ($student["grade"] === $grade) {
            $matches[] = $student;
        }
    }
    return $matches;
}

// Report
echo "šŸ“Š Student Gradebook\n";
echo str_repeat("=", 40) . "\n";
foreach ($students as ["name" => $name, "grade" => $grade, "score" => $score]) {
    echo sprintf("%-8s Grade: %s  Score: %3d\n", $name, $grade, $score);
}
echo str_repeat("-", 40) . "\n";
echo "Average Score: " . number_format(get_average($students), 1) . "\n";

$top = get_highest($students);
echo "Top Student: {$top['name']} ({$top['score']})\n";

$aStudents = get_by_grade($students, "A");
echo "\nA Students: ";
foreach ($aStudents as $s) {
    echo "{$s['name']} ";
}
echo "\n";

šŸ‹ļø Exercise 2: Shopping Cart

Objective: Build a shopping cart using arrays with add, remove, and total functionality.

Instructions:

  1. Create a $cart array (start empty)
  2. Write add_item(array &$cart, string $name, float $price, int $qty): void
  3. Write remove_item(array &$cart, string $name): bool — returns true if removed
  4. Write get_total(array $cart): float — sum of (price Ɨ qty) for all items
  5. Write display_cart(array $cart): void — formatted cart display
  6. Add 4 items, remove 1, and display the final cart with total
šŸ’” Hint

Use the item name as the associative key for easy lookup and removal. Each item stores ["price" => ..., "qty" => ...]. Pass the cart by reference (&$cart) so functions can modify it directly.

āœ… Solution
<?php
function add_item(array &$cart, string $name, float $price, int $qty = 1): void {
    if (isset($cart[$name])) {
        $cart[$name]["qty"] += $qty; // If item exists, add to quantity
    } else {
        $cart[$name] = ["price" => $price, "qty" => $qty];
    }
}

function remove_item(array &$cart, string $name): bool {
    if (isset($cart[$name])) {
        unset($cart[$name]);
        return true;
    }
    return false;
}

function get_total(array $cart): float {
    $total = 0.0;
    foreach ($cart as $item) {
        $total += $item["price"] * $item["qty"];
    }
    return $total;
}

function display_cart(array $cart): void {
    echo "šŸ›’ Shopping Cart\n";
    echo str_repeat("=", 50) . "\n";

    if (empty($cart)) {
        echo "  (empty)\n";
        return;
    }

    foreach ($cart as $name => ["price" => $price, "qty" => $qty]) {
        $subtotal = $price * $qty;
        echo sprintf("  %-20s %2d Ɨ \$%6.2f = \$%7.2f\n",
            $name, $qty, $price, $subtotal);
    }

    echo str_repeat("-", 50) . "\n";
    echo sprintf("  %-20s %18s \$%7.2f\n", "TOTAL", "", get_total($cart));
}

// Build the cart
$cart = [];
add_item($cart, "Widget", 12.99, 3);
add_item($cart, "Gadget", 49.99, 1);
add_item($cart, "Cable", 7.50, 2);
add_item($cart, "Screen Protector", 4.99, 5);

// Remove an item
remove_item($cart, "Cable");

// Display
display_cart($cart);

šŸ‹ļø Exercise 3: Config Merger (Spread & Destructuring)

Objective: Practice the spread operator and destructuring with a configuration system.

Instructions:

  1. Create a $defaults config array with keys: theme, language, items_per_page, show_sidebar, cache_ttl
  2. Create a $user_prefs array that overrides theme and language
  3. Create a $admin_overrides array that overrides cache_ttl
  4. Use the spread operator to merge all three (defaults → user → admin)
  5. Destructure the final config to extract theme and language into variables
  6. Display all settings
āœ… Solution
<?php
$defaults = [
    "theme"          => "light",
    "language"       => "en",
    "items_per_page" => 20,
    "show_sidebar"   => true,
    "cache_ttl"      => 3600,
];

$user_prefs = [
    "theme"    => "dark",
    "language" => "es",
];

$admin_overrides = [
    "cache_ttl" => 7200,
];

// Merge with spread — later values win
$config = [...$defaults, ...$user_prefs, ...$admin_overrides];

// Destructure specific values
["theme" => $theme, "language" => $lang] = $config;

echo "šŸ”§ Final Configuration\n";
echo str_repeat("=", 40) . "\n";
foreach ($config as $key => $value) {
    $display = is_bool($value) ? ($value ? "true" : "false") : $value;
    echo sprintf("  %-18s %s\n", $key, $display);
}
echo "\nActive theme: $theme\n";
echo "Active language: $lang\n";

šŸŽÆ Quick Quiz

Question 1: What index does the first element of a PHP array have?

Question 2: What's the difference between isset($arr["key"]) and array_key_exists("key", $arr)?

Question 3: What does unset($arr[2]) do to an indexed array?

Question 4: Why must you call unset($item) after a foreach ($arr as &$item) loop?

Question 5: When merging associative arrays with the spread operator, what happens to duplicate keys?

Summary

šŸŽ‰ Key Takeaways

  • Indexed arrays — Ordered lists with numeric keys starting at 0; created with []
  • Associative arrays — Key-value pairs with string keys; created with ["key" => "value"]
  • Adding elements — $arr[] = val, array_push(), array_unshift() for beginning
  • Removing elements — array_pop() (end), array_shift() (beginning), unset() (by key), array_splice() (by index, re-indexes)
  • Checking — in_array() for values, array_key_exists() or isset() for keys, count() for size
  • foreach — The primary way to loop arrays; use & for modification (then unset after!)
  • Multidimensional — Arrays of arrays; access with chained brackets: $arr[0]["name"]
  • Destructuring — Unpack values: [$a, $b] = $arr or ["key" => $var] = $arr
  • Spread operator — Merge arrays: [...$a, ...$b]; unpack into function args: func(...$arr)

šŸ“š Additional Resources

šŸš€ What's Next?

Now that you know how to create and work with arrays, it's time to unlock their full power. In Lesson 9: Array Functions, you'll learn PHP's rich library of built-in array functions — array_map, array_filter, array_reduce, sorting, merging, searching, and more.

šŸŽ‰ Great Work!

Arrays are one of PHP's most important tools — you'll use them in virtually every project. Master these fundamentals and the array functions in the next lesson will feel natural!