Перейти к содержанию
Educora
Начальный18 мин5 / 10

Функции, стрелочные функции и замыкания

Пиши функции с типизированными параметрами и типом возвращаемого значения, используй аргументы по умолчанию и именованные аргументы, стрелочные функции и замыкания, обрабатывай массивы с помощью array_map, array_filter и usort.

Проверь себя
В этом уроке ты узнаешь
  • Писать функции с типами параметров и возвращаемого значения и объяснять роль strict_types
  • Использовать аргументы по умолчанию, именованные аргументы и переменное число аргументов
  • Применять array_map, array_filter, array_reduce и usort со стрелочными функциями и замыканиями

Представь, что на сайте магазина цена товара с НДС считается в десяти разных местах. Когда ставка налога изменится, придётся исправлять десять мест — и одно обязательно забудется. Эту проблему решает функция: ты пишешь код один раз, даёшь ему имя и вызываешь где угодно.

Типизированные функции

Функция объявляется ключевым словом function. В современном PHP принято указывать типы параметров (float $price) и тип возвращаемого значения (: float). Функция, которая ничего не возвращает, помечается : void, функция, которая может вернуть и null, — ?string, а параметр, принимающий один из нескольких типов, записывается как int|float.

PHP
<?php

declare(strict_types=1);

function fullPrice(float $price, int $quantity = 1): float
{
    return $price * $quantity;
}

function greet(string $name): string
{
    return "Hello, $name!";
}

echo greet("Aysel"), "\n";
echo fullPrice(2.5, 4), "\n";
echo fullPrice(7.5), "\n";
Ожидаемый результат
Hello, Aysel!
10
7.5

declare(strict_types=1); в первой строке файла включает строгий режим. В нём PHP не позволяет передать строку "5" в параметр типа int и выбрасывает TypeError. Без строгого режима PHP пытается преобразовать значение, и ошибки остаются незамеченными. Единственное исключение: int всегда можно передать в параметр типа float.

Без типов: ошибка скрыта
function add($a, $b)
{
    return $a + $b;
}

echo add("5", "3"); // 8, works by accident
С типами: ошибка видна сразу
declare(strict_types=1);

function add(int $a, int $b): int
{
    return $a + $b;
}

echo add("5", "3"); // TypeError
Типы — это «договор» функции: редактор кода тоже их читает и подсказывает ошибки ещё во время набора.

Аргументы по умолчанию, именованные и переменные

Если у параметра есть значение по умолчанию, его можно не указывать при вызове. Начиная с PHP 8 есть именованные аргументы: написав bold: true, ты пропускаешь промежуточные параметры и передаёшь нужный по имени. ...$numbers собирает любое число аргументов в массив, а при вызове ...$marks «распаковывает» массив в отдельные аргументы.

PHP
<?php

function makeTag(string $text, string $tag = "p", bool $bold = false): string
{
    $inner = $bold ? "<b>$text</b>" : $text;
    return "<$tag>$inner</$tag>";
}

function average(float ...$numbers): float
{
    return array_sum($numbers) / count($numbers);
}

echo makeTag("Hello"), "\n";
echo makeTag("Title", "h1"), "\n";
echo makeTag("Warning", bold: true), "\n";
echo average(4, 5, 3), "\n";
$marks = [5, 5, 4, 4];
echo average(...$marks), "\n";
Ожидаемый результат
<p>Hello</p>
<h1>Title</h1>
<p><b>Warning</b></p>
4
4.5

Анонимные функции и замыкания

В PHP функция — это значение: безымянную функцию можно записать в переменную и вызвать позже. Чтобы видеть внешнюю переменную, анонимная функция «захватывает» её через use ($rate) — такую функцию называют замыканием (closure). Стрелочная функция fn($x) => ... (PHP 7.4) короче: она состоит из одного выражения и захватывает внешние переменные автоматически.

PHP
<?php

$rate = 1.7;

$toAzn = function (float $usd) use ($rate): float {
    return $usd * $rate;
};
$toUsd = fn(float $azn): float => round($azn / $rate, 2);

echo $toAzn(10), "\n";
echo $toUsd(34), "\n";

function makeCounter(): Closure
{
    $count = 0;
    return function () use (&$count): int {
        return ++$count;
    };
}

$next = makeCounter();
$next();
$next();
echo $next(), "\n";
Ожидаемый результат
17
20
3

use ($rate) захватывает копию значения. А use (&$count) ссылается на саму переменную, поэтому счётчик растёт при каждом вызове и продолжает жить после завершения makeCounter. Стрелочные функции всегда захватывают по значению и не могут изменить внешнюю переменную.

Функции внутри функций для массивов

Чаще всего замыкания применяют с функциями для массивов. array_map преобразует каждый элемент, array_filter оставляет элементы, прошедшие проверку, array_reduce «сворачивает» массив в одно значение, а usort сортирует с помощью твоей функции сравнения. Ниже мы добавляем к ценам НДС 18 % и расставляем учеников по баллам.

PHP
<?php

$prices = [12, 45, 7, 30];

$withVat = array_map(fn($p) => $p * 1.18, $prices);
echo implode(", ", $withVat), "\n";

$expensive = array_filter($prices, fn($p) => $p > 10);
print_r($expensive);

$total = array_reduce($prices, fn($sum, $p) => $sum + $p, 0);
echo $total, "\n";

$students = [
    ["name" => "Murad", "score" => 78],
    ["name" => "Leyla", "score" => 95],
    ["name" => "Elvin", "score" => 86],
];
usort($students, fn($a, $b) => $b["score"] <=> $a["score"]);
echo implode(", ", array_column($students, "name")), "\n";
Ожидаемый результат
14.16, 53.1, 8.26, 35.4
Array
(
    [0] => 12
    [1] => 45
    [3] => 30
)
94
Leyla, Elvin, Murad

Главное

  • Указывай типы параметров и возвращаемого значения; declare(strict_types=1) запрещает автоматические преобразования и выявляет ошибки.
  • Параметры со значением по умолчанию можно опускать, а именованные аргументы (bold: true) позволяют пропускать промежуточные.
  • ...$args собирает аргументы в массив, а ...$array при вызове распаковывает массив в аргументы.
  • Замыкание захватывает внешние переменные через use (по ссылке — с &), а стрелочная функция fn — автоматически и по значению.
  • array_map, array_filter, array_reduce и usort с замыканиями обрабатывают массивы коротко и понятно; array_filter сохраняет ключи.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
В файле есть declare(strict_types=1);. Что произойдёт, если вызвать function add(int $a, int $b): int как add("5", "3")?