Lesson 5 / الدرس 5

Naming a piece of work / تسمية قطعة عمل

A function is the smallest unit you can name, test and trust. The discipline that matters is not writing them — it is making each one depend only on what it was given, so that reading its first line tells you everything it can touch.

الدالة أصغر وحدة تستطيع تسميتها واختبارها والوثوق بها. والانضباط المهم ليس كتابتها — بل جعل كلٍّ منها تعتمد على ما أُعطيته فقط، حتى تخبرك قراءة سطرها الأول بكل ما تستطيع مسّه.

You met functions in the basics course. PHP adds one thing worth taking seriously: you can declare what goes in and what comes out, and PHP will enforce it. A typed signature is documentation that cannot go stale, because the code stops working when it becomes untrue.

<?php
declare(strict_types=1);   // first line of every file on this site

function hours(array $course): int
{
    return array_sum(array_column($course['chapters'], 'hours'));
}

// Optional arguments have defaults, and go last.
function summary(string $text, int $limit = 120): string
{
    return strlen($text) <= $limit
        ? $text
        : substr($text, 0, $limit - 1) . '…';
}

// ?string means 'a string or null'. Being explicit about null is
// most of what type declarations buy you.
function findCourse(array $all, string $slug): ?array
{
    return $all[$slug] ?? null;
}
declare(strict_types=1) is what makes those declarations mean anything. Without it PHP quietly converts "16" to 16 for you; with it, passing a string where an int was declared is an error at the call. Put it at the top of every file — every file in this repository has it.

A function should not reach outside itself

<?php
// Bad. This function reads $_POST directly.
function saveSubmission(): void
{
    $title = $_POST['title'];
    // …
}

// Good. Everything it uses arrives through the door.
function saveSubmission(string $title, int $studentId): void
{
    // …
}

// The caller does the reaching, once, where you can see it:
saveSubmission($_POST['title'] ?? '', $user['id']);
The first version cannot be tested without inventing a fake request, cannot be reused from a command-line script, and gives no clue from its signature what it depends on. The second can be called with two values you made up, which is the whole reason the testing course could test anything at all.

Small closures, for the array functions

  • fn($x) => $x * 2 — the short form. One expression, and it can see the variables around it automatically. This is what you pass to array_map and array_filter.
  • function ($x) use ($limit) { … } — the long form, for more than one statement. It sees nothing from around it unless you list it in use, which catches a whole class of mistake.
  • If a closure grows past about three lines, it wanted a name. Give it one and call it — usort($courses, byTitle(...)) reads better than five anonymous lines wedged inside a call.

Check yourself / اختبر نفسك

1. What does declare(strict_types=1) change?

2. Why should a function take $_POST values as arguments rather than reading $_POST itself?

3. What is wrong with global $db inside a function?