PHP Zero to Advanced #5: Arrays & Array Functions
Arrays are the workhorse of PHP. A single PHP array can act as a list, a dictionary (map), a stack, a queue, or a nested tree of data. Form submissions, database rows, JSON payloads, and configuration files all end up as arrays. In this part you will learn how to create and manipulate arrays, and how to use the built-in functions that save you from writing loops by hand.
Indexed Arrays
An indexed array uses automatic integer keys starting at 0. The short syntax [] is preferred over the older array().
<?php
$colors = ["red", "green", "blue"];
echo $colors[0] . "\n";
echo count($colors) . "\n";
$colors[] = "yellow"; // append
$colors[1] = "lime"; // update
print_r($colors);
echo $colors[count($colors) - 1] . "\n"; // last element
Output:
red
3
Array
(
[0] => red
[1] => lime
[2] => blue
[3] => yellow
)
yellow
print_r() prints arrays in a human-friendly format and is one of the most useful debugging tools in PHP.
Associative Arrays
Associative arrays use string keys, which makes them perfect for records like a user or a product. Use isset() or array_key_exists() to check if a key exists. The difference: isset() returns false when the value is null.
<?php
$user = [
"name" => "Siti",
"email" => "siti@example.com",
"age" => 28,
];
echo $user["name"] . "\n";
$user["city"] = "Surabaya"; // add a new key
unset($user["age"]); // remove a key
var_dump(isset($user["age"]), array_key_exists("email", $user));
print_r($user);
Output:
Siti
bool(false)
bool(true)
Array
(
[name] => Siti
[email] => siti@example.com
[city] => Surabaya
)
Nested (multidimensional) arrays
Arrays can contain other arrays. This is exactly how a list of database rows or a decoded JSON response looks.
<?php
$students = [
["name" => "Budi", "score" => 78, "skills" => ["PHP", "SQL"]],
["name" => "Siti", "score" => 92, "skills" => ["PHP", "JS", "Go"]],
];
echo $students[1]["name"] . " knows " . count($students[1]["skills"]) . " languages\n";
foreach ($students as $student) {
echo "{$student['name']}: " . implode(", ", $student['skills']) . "\n";
}
Output:
Siti knows 3 languages
Budi: PHP, SQL
Siti: PHP, JS, Go
Destructuring and spreading
Destructuring lets you unpack values into variables in one line. The spread operator ... merges arrays; later keys overwrite earlier ones.
<?php
[$first, $second] = ["Budi", "Siti"];
echo "$first and $second\n";
['name' => $name, 'city' => $city] = ['name' => 'Budi', 'city' => 'Jakarta'];
echo "$name lives in $city\n";
// Swap two variables
$a = 1;
$b = 2;
[$a, $b] = [$b, $a];
echo "a=$a, b=$b\n";
// Spread operator works with string keys since PHP 8.1
$defaults = ['theme' => 'light', 'lang' => 'en'];
$custom = ['lang' => 'id'];
print_r([...$defaults, ...$custom]);
Output:
Budi and Siti
Budi lives in Jakarta
a=2, b=1
Array
(
[theme] => light
[lang] => id
)
Searching and Slicing
Pass true as the third argument to in_array() and array_search() to use strict comparison. It avoids surprises like in_array("abc", [0]).
<?php
$fruits = ["apple", "banana", "mango"];
var_dump(in_array("mango", $fruits, true));
var_dump(array_search("banana", $fruits, true));
print_r(array_keys(["a" => 1, "b" => 2]));
print_r(array_values(["a" => 1, "b" => 2]));
print_r(array_slice([10, 20, 30, 40, 50], 1, 3));
print_r(array_merge([1, 2], [3, 4]));
Output:
bool(true)
int(1)
Array
(
[0] => a
[1] => b
)
Array
(
[0] => 1
[1] => 2
)
Array
(
[0] => 20
[1] => 30
[2] => 40
)
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
Transforming: array_map, array_filter, array_reduce
These three functions replace most hand-written loops:
array_map($fn, $array)transforms every element and returns a new array of the same size.array_filter($array, $fn)keeps only elements for which the callback returnstrue.array_reduce($array, $fn, $initial)combines all elements into a single value.
Watch the argument order: array_map takes the callback first, while array_filter and array_reduce take the array first.
<?php
$prices = [12000, 5000, 30000, 7500];
$withTax = array_map(fn($p) => $p * 1.1, $prices);
print_r($withTax);
$expensive = array_filter($prices, fn($p) => $p >= 10000);
print_r($expensive); // keys are preserved!
print_r(array_values($expensive)); // re-index
$total = array_reduce($prices, fn($carry, $p) => $carry + $p, 0);
echo "Total: $total\n";
Output:
Array
(
[0] => 13200
[1] => 5500
[2] => 33000
[3] => 8250
)
Array
(
[0] => 12000
[2] => 30000
)
Array
(
[0] => 12000
[1] => 30000
)
Total: 54500
Notice that array_filter keeps the original keys (0 and 2). If you need a clean list, wrap it with array_values(), otherwise json_encode will turn it into a JSON object instead of an array.
array_column
array_column extracts one field from a list of records, optionally keyed by another field. Combined with array_filter, it reads almost like a query.
<?php
$users = [
["id" => 1, "name" => "Budi", "active" => true],
["id" => 2, "name" => "Siti", "active" => false],
["id" => 3, "name" => "Andi", "active" => true],
];
print_r(array_column($users, "name"));
print_r(array_column($users, "name", "id"));
$activeNames = array_column(
array_filter($users, fn($u) => $u["active"]),
"name"
);
echo implode(", ", $activeNames) . "\n";
Output:
Array
(
[0] => Budi
[1] => Siti
[2] => Andi
)
Array
(
[1] => Budi
[2] => Siti
[3] => Andi
)
Budi, Andi
Sorting
PHP's sort functions modify the array in place and return true, not the sorted array. Choose the function based on whether you want to keep keys:
| Function | Sorts by | Keeps keys? |
|---|---|---|
sort / rsort | Value | No |
asort / arsort | Value | Yes |
ksort / krsort | Key | Yes |
usort / uasort | Custom callback | No / Yes |
<?php
$numbers = [5, 3, 9, 1];
sort($numbers); // sorts in place, returns bool
echo implode(", ", $numbers) . "\n";
rsort($numbers);
echo implode(", ", $numbers) . "\n";
$ages = ["Budi" => 30, "Siti" => 25, "Andi" => 35];
asort($ages); // sort by value, keep keys
print_r($ages);
ksort($ages); // sort by key
print_r($ages);
Output:
1, 3, 5, 9
9, 5, 3, 1
Array
(
[Siti] => 25
[Budi] => 30
[Andi] => 35
)
Array
(
[Andi] => 35
[Budi] => 30
[Siti] => 25
)
For custom ordering, usort combined with the spaceship operator <=> is the idiomatic approach:
<?php
$products = [
["name" => "Laptop", "price" => 12000000],
["name" => "Mouse", "price" => 150000],
["name" => "Monitor", "price" => 2500000],
];
usort($products, fn($a, $b) => $a["price"] <=> $b["price"]);
foreach ($products as $p) {
echo str_pad($p["name"], 10) . number_format($p["price"], 0, ',', '.') . "\n";
}
// Descending: swap $a and $b
usort($products, fn($a, $b) => $b["price"] <=> $a["price"]);
echo "Most expensive: " . $products[0]["name"] . "\n";
Output:
Mouse 150.000
Monitor 2.500.000
Laptop 12.000.000
Most expensive: Laptop
More handy functions
<?php
$tags = ["php", "web", "php", "api", "web"];
print_r(array_unique($tags));
print_r(array_count_values($tags));
print_r(array_combine(["a", "b"], [1, 2]));
print_r(array_chunk([1, 2, 3, 4, 5], 2));
echo json_encode(["name" => "Budi", "skills" => ["PHP", "SQL"]]) . "\n";
Output:
Array
(
[0] => php
[1] => web
[3] => api
)
Array
(
[php] => 2
[web] => 2
[api] => 1
)
Array
(
[a] => 1
[b] => 2
)
Array
(
[0] => Array
(
[0] => 1
[1] => 2
)
[1] => Array
(
[0] => 3
[1] => 4
)
[2] => Array
(
[0] => 5
)
)
{"name":"Budi","skills":["PHP","SQL"]}
Common Mistakes
- Calling
array_filterwithout a callback on data containing0or"0". All falsy values are removed, which may delete valid data:
<?php
$values = [0, 1, "", "hello", null, "0", false, 42];
print_r(array_filter($values)); // no callback: removes ALL falsy values
Output:
Array
(
[1] => 1
[3] => hello
[7] => 42
)
- Assigning the result of
sort().$sorted = sort($arr);gives youtrue, not an array. - Reading a missing key.
$user["phone"]raises a warning if the key does not exist. Use$user["phone"] ?? null. - Using
array_mergewith numeric keys and expecting them to be preserved. Numeric keys are renumbered. Use the+operator if you need to keep them. - Loose search. Always pass
trueas the strict flag toin_arrayandarray_search.
Exercise
Given a list of orders, calculate the total spending per customer, sort from highest to lowest, and show only customers who spent at least Rp 100.000.
<?php
$orders = [
["customer" => "Budi", "total" => 150000],
["customer" => "Siti", "total" => 320000],
["customer" => "Budi", "total" => 90000],
["customer" => "Andi", "total" => 45000],
["customer" => "Siti", "total" => 80000],
];
$perCustomer = [];
foreach ($orders as $order) {
$perCustomer[$order["customer"]] = ($perCustomer[$order["customer"]] ?? 0) + $order["total"];
}
arsort($perCustomer);
$big = array_filter($perCustomer, fn($total) => $total >= 100000);
foreach ($big as $customer => $total) {
echo "$customer: " . number_format($total, 0, ',', '.') . "\n";
}
Output:
Siti: 400.000
Budi: 240.000
Challenge: rewrite the grouping step with array_reduce instead of foreach.
Summary
- PHP arrays can be indexed lists, associative maps, or nested structures.
- Use
print_r/var_dumpto inspect,isset/??to read safely. array_map,array_filter,array_reduce, andarray_columnreplace many manual loops.- Sorting functions work in place; pick the one that keeps keys when you need them, and use
usortwith<=>for custom order.
In Part 6 we will put arrays to work with real user input: handling HTML forms, $_GET and $_POST, validation, and safely escaping output.
Related articles
PostgreSQL Zero to Advanced #5: Aggregation, GROUP BY & HAVING
Turn raw orders into business numbers: total revenue, average order value, sales per month and top customers using COUNT, SUM, AVG, GROUP BY, HAVING and FILTER.
Ruby on Rails Zero to Advanced #5: Routing, Controllers & Views
Follow a request through Rails: define routes, use resources, read params in controllers, and render dynamic HTML with ERB views and partials.
Python Zero to Advanced #5: Lists, Tuples, Dictionaries, Sets & Comprehensions
Master Python's built-in collections: lists, tuples, dictionaries, and sets, when to use each, slicing, unpacking, and writing concise list, dict, and set comprehensions.