Originally published on decentphp.dev — part of the Writing decent PHP course. View this lesson on the original site.

A collection name should tell you "what's in it" and "what's already been done to it"

Collections (arrays, iterables, lists, maps) carry two pieces of information you usually want at a glance:

  • what it contains — users, orders, invoices
  • what filtering, grouping, or transformation has already been applied — active, pending, byEmail, groupedByCustomer

A name like $data or $list throws both of those away.

Before

The names tell you it is "some collection of something", and that is about it.

<?php

function emailReminders(array $list, array $items, array $map): void
{
    foreach ($list as $i) {
        if (! $i['active']) {
            continue;
        }

        foreach ($items as $row) {
            if ($row['user_id'] !== $i['id']) {
                continue;
            }

            if (! isset($map[$row['status']])) {
                continue;
            }

            sendEmail($i['email'], $map[$row['status']]);
        }
    }
}

You can work it out, but you have to read the loop body to discover:

  • $list is users
  • $items is invoices
  • $map is keyed by status, holding email templates
  • only active users get processed
  • the loop variable $i could mean anything

After

The names carry the meaning. The loop body just confirms what you already expect.

<?php

function emailOverdueRemindersToActiveUsers(
    array $activeUsers,
    array $overdueInvoices,
    array $emailTemplatesByStatus
): void {
    foreach ($activeUsers as $activeUser) {
        foreach ($overdueInvoices as $overdueInvoice) {
            if ($overdueInvoice['user_id'] !== $activeUser['id']) {
                continue;
            }

            $template = $emailTemplatesByStatus[$overdueInvoice['status']] ?? null;
            if ($template === null) {
                continue;
            }

            sendEmail($activeUser['email'], $template);
        }
    }
}

Now the function signature alone tells you most of the story. The loop variables ($activeUser, $overdueInvoice) are singular forms of the collections they came from, so the relationship is obvious.

Why this version is better

  • the qualifier (active, overdue) is in the name, not buried in an if
  • you can tell a list from a keyed map without looking inside (emailTemplatesByStatus is clearly keyed by status)
  • the loop variable echoes the collection name, so you never lose track of what you are iterating over
  • the function signature is self-describing — no comments needed

Collection naming patterns

Pattern When to use Example
Plural noun a list of things $users, $invoices, $tags
Adjective + plural a filtered subset $activeUsers, $overdueInvoices, $expiredTokens
xxxBy<Key> a map keyed by something $usersById, $ordersByCustomer, $templatesByStatus
xxxFor<Context> a subset scoped to something $invoicesForCustomer, $rolesForUser
xxxIn<Range> scoped by time or range $ordersInLastWeek, $loginsInToday
groupedXxx grouped, usually nested $groupedOrdersByMonth
sortedXxx already sorted $sortedInvoicesByDueDate
recentXxx, topXxx already trimmed $recentOrders, $topCustomers

Singular vs plural

A collection is plural. The thing inside it is singular. Keep them in sync.

foreach ($activeUsers as $activeUser) { ... }
foreach ($overdueInvoices as $overdueInvoice) { ... }
foreach ($pendingApprovals as $pendingApproval) { ... }

Avoid foreach ($users as $u) or foreach ($items as $row). Two extra characters save the next reader a translation step.

Names to avoid

  • $list, $items, $rows, $results, $data — generic and forgettable
  • $arr, $collection — describes the type, not the contents
  • $userArray, $invoiceList — the type suffix adds noise; the plural already tells you it is a collection
  • $user for a list of users — singular for a plural is the most common naming bug in PHP code

A small collection smell test

Look at the name and ask:

  1. Can I tell what is in it without opening the function?
  2. Can I tell whether it has been filtered, sorted, or keyed?
  3. Does the loop variable name match the singular form?

If any answer is "no", the name is leaving work for the reader.

From Writing decent PHP — Naming variables, functions, classes, and files well