Arrays, Functions, Iteration, and Callbacks
Arrays and functions do most of the heavy lifting in PHP. PHP's arrays are wonderfully flexible, which is exactly why it's easy to lose track of what shape your data is in. A little discipline here goes a long way.
Lists and associative arrays
<?php
declare(strict_types=1);
$vehicleIds = [10, 14, 21];
$customer = [
'id' => 42,
'name' => 'Example Company',
'status' => 'active',
];
echo $customer['name'];
If the same array shape gets passed through a bunch of layers, either document it with PHPDoc or turn it into a small value object. Future you won't remember which keys are guaranteed to exist.
Iteration
<?php
declare(strict_types=1);
$totals = [12500, 18750, 9900];
$grandTotal = 0;
foreach ($totals as $totalCents) {
$grandTotal += $totalCents;
}
echo number_format($grandTotal / 100, 2);
Map, filter, and reduce
<?php
declare(strict_types=1);
$customers = [
['id' => 1, 'name' => 'Acme', 'active' => true],
['id' => 2, 'name' => 'Northwind', 'active' => false],
['id' => 3, 'name' => 'Contoso', 'active' => true],
];
$activeNames = array_map(
static fn (array $customer): string => $customer['name'],
array_filter(
$customers,
static fn (array $customer): bool => $customer['active'] === true
)
);
print_r(array_values($activeNames));
Readability rule: A simple
foreach is often clearer than deeply nested functional calls. Choose the form that communicates intent.Typed functions
<?php
declare(strict_types=1);
function calculateLineTotal(int $quantity, int $unitPriceCents): int
{
if ($quantity < 1) {
throw new InvalidArgumentException('Quantity must be positive.');
}
return $quantity * $unitPriceCents;
}
Named arguments and defaults
<?php
declare(strict_types=1);
function createAuditEntry(
int $actorId,
string $action,
?int $recordId = null,
array $context = []
): array {
return compact('actorId', 'action', 'recordId', 'context');
}
$entry = createAuditEntry(
actorId: 7,
action: 'invoice.sent',
recordId: 214
);
Named arguments are great when a function has a bunch of optional parameters and you only want to set one or two. One catch: once people call your library with named arguments, renaming a parameter becomes a breaking change.
Generators for big data sets
<?php
declare(strict_types=1);
function readCsvRows(string $filename): Generator
{
$handle = fopen($filename, 'rb');
if ($handle === false) {
throw new RuntimeException('Unable to open CSV file.');
}
try {
while (($row = fgetcsv($handle)) !== false) {
yield $row;
}
} finally {
fclose($handle);
}
}
foreach (readCsvRows('customers.csv') as $row) {
// Process one row at a time without loading the entire file.
}
