Создание функций в PHP: от базового синтаксиса до продвинутых приёмов

Раздел: Программирование на PHP -> Функции и их объявление

Функции позволяют группировать повторяющийся код и делают программы модульными и читаемыми. В PHP функция объявляется ключевым словом function, после которого указывается имя, круглые скобки для параметров и блок кода в фигурных скобках.

Базовое объявление функции с возвратом значения

Наиболее распространённый способ - функция принимает аргументы, выполняет действия и возвращает результат через return.


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

echo sum(3, 5); // 8
  

создания функции php (создание функций в php)

В примере объявлена функция sum с двумя параметрами типа int и указанием возвращаемого типа. После вызова она складывает числа и выводит результат.

Типичные ошибки и их решение

  • Забыта точка с запятой после выражения - PHP выдаст синтаксическую ошибку. Следует проверять окончание каждой строки.
  • Несоответствие типа аргумента: если передать строку, в режиме строгой типизации (declare(strict_types=1)) будет вызвано исключение TypeError. Без строгой типизации PHP попытается преобразовать значение, что может дать неожиданный результат. Решение - либо приводить типы явно, либо включать строгую проверку.
  • Переменные, объявленные вне функции, недоступны внутри без ключевого слова global или передачи по ссылке. Это типичная проблема области видимости. Решение - передавать необходимые данные через параметры.

Различные варианты создания функций

Как задать параметры по умолчанию, чтобы аргумент можно было не указывать?

Параметрам можно присвоить значения по умолчанию. Тогда при вызове без соответствующего аргумента используется стандартное значение.


function greet(string $name = 'гость'): string {
    return "Привет, $name!";
}
echo greet(); // Привет, гость!
echo greet('Анна'); // Привет, Анна!
  

Важно, что параметры с умолчанием должны располагаться после обязательных параметров.

Если поменять порядок (обязательный параметр после необязательного), PHP выдаст предупреждение. Решение - всегда размещать параметры с умолчанием в конце списка.

Как изменить внешнюю переменную внутри функции, используя передачу по ссылке?

Символ & перед именем параметра позволяет передавать переменную по ссылке - изменения внутри функции отразятся на внешней переменной.


function addExclamation(string &$text): void {
    $text .= '!';
}
$str = 'Привет';
addExclamation($str);
echo $str; // Привет!
  

Зачем это нужно? Для модификации больших массивов или объектов без копирования, а также когда функция должна изменить аргумент, не возвращая новое значение.

Опасность: случайное изменение внешних данных может привести к побочным эффектам. Решение - чётко документировать такие функции и использовать ссылки только при необходимости.

Как обеспечить строгую типизацию для аргументов и возвращаемого значения?

С помощью директивы declare(strict_types=1) в начале файла PHP включает строгую проверку типов. Также можно указывать типы для параметров и возврата.


declare(strict_types=1);

function multiply(float $x, float $y): float {
    return $x * $y;
}

echo multiply(2.5, 4); // Ошибка: Argument #2 must be of type float
  

В данном примере передача целого числа 4 вызовет TypeError, так как строгая типизация не допускает неявного преобразования.

Частая ошибка - забыть подключить declare(strict_types=1), и PHP автоматически приводит типы. Если нужна строгая проверка, необходимо явно указать директиву в начале каждого файла. Включение происходит на уровне файла.

Как создать анонимную функцию (замыкание) для использования в обратных вызовах?

Анонимные функции создаются без имени и могут быть присвоены переменной или переданы как аргумент. Для доступа к переменным из внешней области используется конструкция use.


$factor = 2;
$multiply = function (int $a) use ($factor): int {
    return $a * $factor;
};
echo $multiply(5); // 10
  

С помощью use захватывается значение $factor. По умолчанию захват происходит по значению, для изменения внешней переменной нужно добавить &.

Если не использовать use, переменная $factor будет недоступна внутри функции. Решение - явно перечислить нужные переменные в списке use. Также стоит помнить, что захват переменных происходит на момент определения, а не вызова функции.

Как использовать стрелочные функции (arrow functions) для краткой записи?

Стрелочные функции появились в PHP 7.4 и позволяют записать простое выражение без фигурных скобок. Они автоматически захватывают переменные из внешней области по значению.


$numbers = [1, 2, 3, 4];
$squared = array_map(fn($n) => $n * $n, $numbers);
print_r($squared); // [1, 4, 9, 16]
  

Конструкция fn($n) => $n * $n эквивалентна анонимной функции, но не требует явного use.

Стрелочная функция может содержать только одно выражение (не более одного оператора). Если нужно несколько действий, следует использовать обычную анонимную функцию.

Как создать функцию с переменным числом аргументов, используя оператор ... (spread)?

Оператор ... перед параметром собирает все переданные аргументы в массив.


function concatAll(string ...$parts): string {
    return implode(' ', $parts);
}

echo concatAll('PHP', 'это', 'здорово'); // PHP это здорово
  

Можно также использовать ... при вызове функции для распаковки массива.

Если функция уже имеет обязательные параметры, вариативный параметр должен быть последним. Иначе PHP выдаст ошибку синтаксиса.

Как реализовать рекурсивную функцию, вызывающую саму себя?

Рекурсия полезна для задач, разделяемых на подзадачи, например вычисление факториала или обход дерева.


function factorial(int $n): int {
    if ($n <= 1) {
        return 1;
    }
    return $n * factorial($n - 1);
}
echo factorial(5); // 120
  

Обязательно должно быть условие завершения рекурсии (базовый случай), иначе функция будет вызывать себя бесконечно.

Глубокие рекурсии могут превысить лимит стека вызовов (обычно 256 или 1000 в зависимости от настроек). Решение - увеличить лимит с помощью ini_set('xdebug.max_nesting_level', ...) или заменить на итерацию.

Как создать функцию-генератор для экономии памяти при работе с большими наборами данных?

Генераторы используют ключевое слово yield, возвращая значение по одному, не загружая всё в память.


function rangeGenerator(int $start, int $end): Generator {
    for ($i = $start; $i <= $end; $i++) {
        yield $i;
    }
}

foreach (rangeGenerator(1, 3) as $num) {
    echo $num . ' ';
}
// 1 2 3
  

Генераторы возвращают объект класса Generator, который можно использовать в цикле.

Если в генераторе не будет ни одного yield, он не сгенерирует значений. Также генератор нельзя переиспользовать - после завершения итераций потребуется создать новый экземпляр.

Расширенные примеры создания функций

Пример

// Пример 1: Комбинация строгой типизации, значений по умолчанию и ссылок
declare(strict_types=1);

function createUser(string $name, string $email = 'unknown@mail.com', ?array &$errors = null): array {
    $user = ['name' => $name, 'email' => $email];
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        if ($errors !== null) {
            $errors[] = 'Некорректный email';
        }
        return [];
    }
    return $user;
}

$err = [];
$user = createUser('Иван', 'invalid', $err);
print_r($user); // []
print_r($err); // [0 => 'Некорректный email']
Array
(
)
Array
(
    [0] => Некорректный email
)

Функция возвращает массив пользователя, а при ошибке заполняет массив $errors, переданный по ссылке. Используются типы, значение по умолчанию для email и nullable тип для параметра ссылки.

Пример

// Пример 2: Анонимная функция как callback для array_filter
$numbers = [2, 3, 5, 8, 13, 21];
$threshold = 10;
$filtered = array_filter($numbers, function($item) use ($threshold) {
    return $item > $threshold;
});
print_r($filtered); // [5 => 13, 6 => 21]
Array
(
    [5] => 13
    [6] => 21
)

Анонимная функция захватывает внешнюю переменную $threshold через use. Индексы сохраняются, так как array_filter не переиндексирует массив.

Пример

// Пример 3: Рекурсия с мемоизацией для чисел Фибоначчи
function fibMemo(int $n, array &$cache = []): int {
    if (isset($cache[$n])) {
        return $cache[$n];
    }
    if ($n === 0 || $n === 1) {
        $cache[$n] = $n;
        return $n;
    }
    $result = fibMemo($n - 1, $cache) + fibMemo($n - 2, $cache);
    $cache[$n] = $result;
    return $result;
}

echo fibMemo(10); // 55
55

Массив $cache хранит уже вычисленные значения, что позволяет избежать экспоненциального роста числа вызовов. Без мемоизации то же вычисление fib(10) потребовало бы 177 рекурсивных вызовов.

Пример

// Пример 4: Генератор с отправкой значений (send)
function coroutine() {
    $output = yield;
    echo "Получено: $output\n";
    $output = yield 'Ответ на первое значение';
    echo "Снова получено: $output\n";
    yield 'Готово';
}

$gen = coroutine();
echo $gen->current() . "\n";        // null?
$gen->send('Привет');                // выведет 'Получено: Привет'
echo $gen->current() . "\n";        // 'Ответ на первое значение'
$gen->send('Как дела?');            // 'Снова получено: Как дела?'
echo $gen->current() . "\n";        // 'Готово'

Получено: Привет
Ответ на первое значение
Снова получено: Как дела?
Готово

Генератор может не только порождать значения, но и получать их извне через метод send(). Это позволяет создавать корутины - сопрограммы.

Пример

// Пример 5: Функция высшего порядка, возвращающая замыкание
function multiplier(float $factor): Closure {
    return function (float $number) use ($factor): float {
        return $number * $factor;
    };
}

$double = multiplier(2);
$triple = multiplier(3);
echo $double(5) . ' ' . $triple(5); // 10 15
10 15

Функция multiplier создает и возвращает новую анонимную функцию, «запомнившую» множитель через use. Это пример частичного применения каррирования.

Пример

// Пример 6: Использование variadic с комбинированными типами
function mergeArrays(string $separator, array ...$arrays): string {
    $result = [];
    foreach ($arrays as $arr) {
        array_push($result, ...$arr);
    }
    return implode($separator, $result);
}

echo mergeArrays('-', [1, 2], [3, 4], [5]); // 1-2-3-4-5
1-2-3-4-5

Оператор ... как в определении функции (собирает массивы), так и при вызове array_push(...$arr) распаковывает массивы. Функция принимает произвольное количество массивов и объединяет их через разделитель.

Создание функций в PHP - comments

En
создания функции php (php)