PHP Strings and Arrays
AI-assistedDrafted with AI assistance and reviewed before publishing; examples are version-pinned. Verify against official docs when in doubt.
A PHP array is an ordered map: it serves as both list and dictionary, and the function library around it is enormous. This chapter covers the part you actually reach for.
Indexed and associative arrays
$colors = ["red", "green", "blue"]; // keys 0, 1, 2
$user = ["name" => "ann", "age" => 30]; // string keys
Appending:
$colors[] = "yellow"; // next free integer key
$user["city"] = "Shenzhen"; // explicit key
Arrays preserve insertion order, and you can rely on it:
$user["role"] = "admin";
foreach ($user as $key => $value) {
echo "$key: $value\n";
}
// name: ann
// age: 30
// city: Shenzhen
// role: admin
Iteration
$colors = ["red", "green", "blue"];
foreach ($colors as $color) {
echo $color . "\n";
}
foreach ($colors as $index => $color) {
echo "$index => $color\n";
}
foreach iterates over a copy: modifying $colors inside the loop does not change the sequence being walked.
$nums = [1, 2, 3];
foreach ($nums as $n) {
$nums[] = $n * 10; // still exactly 3 iterations
}
To modify values, iterate by reference — and always unset afterwards:
foreach ($nums as &$n) {
$n *= 2;
}
unset($n); // required, or reusing $n later corrupts the last element
Array functions you use daily
$nums = [3, 1, 2];
count($nums); // 3
in_array(2, $nums); // true — loose comparison by default
in_array(2, $nums, true); // strict, prefer this
array_keys($nums); // [0, 1, 2]
array_values($nums); // [3, 1, 2]
sort($nums); // sorts in place, returns true → [1, 2, 3]
rsort($nums); // descending, in place
sort() modifies the array and returns a bool. Never write $sorted = sort($nums) — you get true. Copy the array first if you need the original.
array_map / array_filter / array_reduce
$nums = [1, 2, 3];
$doubled = array_map(fn($n) => $n * 2, $nums); // [2, 4, 6]
$evens = array_filter($nums, fn($n) => $n % 2 === 0); // [1 => 2]
$total = array_reduce($nums, fn($carry, $n) => $carry + $n, 0); // 6
Two things to remember:
array_filterpreserves keys, so the result may not be a list. Reindex witharray_values():
$evens = array_values(array_filter($nums, fn($n) => $n % 2 === 0)); // [2]
- Arrow functions (
fn()) capture outer variables automatically; anonymousfunction ()closures needuse:
$factor = 10;
array_map(function ($n) use ($factor) {
return $n * $factor;
}, $nums);
Merging: + versus array_merge
They behave differently, and picking the wrong one silently drops data:
$a = ["name" => "ann", "age" => 30];
$b = ["age" => 31, "city" => "Shenzhen"];
array_merge($a, $b);
// ["name" => "ann", "age" => 31, "city" => "Shenzhen"] — later wins
$a + $b;
// ["name" => "ann", "age" => 30, "city" => "Shenzhen"] — left wins
On indexed arrays: array_merge appends and renumbers, while + keeps the left side and discards overlapping keys.
[1, 2] + [3, 4]; // [1, 2]
array_merge([1, 2], [3, 4]); // [1, 2, 3, 4]
Use array_merge to append lists, + to merge configuration where existing values win.
String functions
$s = "hello world";
strlen($s); // 11 — bytes
str_contains($s, "world"); // true (PHP 8+)
str_starts_with($s, "he"); // true
str_ends_with($s, "ld"); // true
strpos($s, "world"); // 6, or false when not found
strtoupper($s); // "HELLO WORLD"
strtolower($s); // "hello world"
trim(" hi "); // "hi"
str_replace("world", "PHP", $s); // "hello PHP"
substr($s, 0, 5); // "hello"
explode(" ", $s); // ["hello", "world"]
implode("-", ["a", "b"]); // "a-b"
Two rules that come up constantly:
strposreturnsfalsewhen not found and0when found at position 0. Compare with!== false:
if (strpos($s, "hello") !== false) {
echo "found"; // "hello" is at 0 — an == check would wrongly say "not found"
}
strlencounts bytes, so UTF-8 text is longer than it looks:
strlen("你好"); // 6
mb_strlen("你好", "UTF-8"); // 2
mb_substr("你好世界", 0, 2, "UTF-8"); // "你好"
Use the mb_* family for anything user-facing (the mbstring extension is enabled in mainstream distributions).
Converting between strings and arrays
$csv = "a,b,c";
$parts = explode(",", $csv); // ["a", "b", "c"]
$back = implode(",", $parts); // "a,b,c"
// trim and drop empty entries
$tags = array_values(array_filter(
array_map("trim", explode(",", "php, , js, "))
));
// ["php", "js"]
Worked example: filter, total, format
<?php
declare(strict_types=1);
$orders = [
["id" => 1, "amount" => 120, "paid" => true],
["id" => 2, "amount" => 80, "paid" => false],
["id" => 3, "amount" => 200, "paid" => true],
];
$paid = array_filter($orders, fn($o) => $o["paid"]);
$total = array_reduce($paid, fn($carry, $o) => $carry + $o["amount"], 0);
$ids = implode(", ", array_map(fn($o) => "#" . $o["id"], $paid));
echo "Paid orders: {$ids}" . PHP_EOL;
echo "Total: {$total}" . PHP_EOL;
// Paid orders: #1, #3
// Total: 320
Note that $paid still has keys 0 and 2 — fine for array_reduce and array_map, but reindex with array_values() if you need a JSON list.
Common mistakes
1. Treating sort()’s return value as the sorted array
It returns true and sorts in place.
2. Checking strpos() with == false
Position 0 is misread as “not found”. Use !== false.
3. Using strlen() for visible length
It counts bytes. Use mb_strlen().
4. Assuming array_filter reindexes
Wrap it in array_values() when the result must be a list.
5. Expecting edits inside foreach to affect iteration
You are walking a copy. Use & to modify values, then unset.
6. Expecting + to let the right side override
+ keeps the left operand; use array_merge for override semantics.
Summary
- PHP arrays are ordered maps;
foreachwalks a copy. - Transform with
array_map/array_filter/array_reduce; reindex after filtering. - Merge lists with
array_merge, keep existing keys with+. - Search with
str_contains(PHP 8+), and always comparestrposagainst!== false. - Use
mb_*for any multibyte text.
Next: conditionals and loops, including match and the alternative template syntax.