Функции и include/require
Функции спасают от копипасты. А include/require спасают от файла на 500 строк (ну... хотя бы помогают).
Обычные функции
<?php
function greet(string $name): string {
return "Привет, $name!";
}
echo greet('Иван'); // Привет, Иван!
Функция без return вернёт null:
<?php
function sayHello(string $name): void {
echo "Hello, $name!\n";
// return ничего не возвращаем - void
}
Аргументы по умолчанию
<?php
function createUser(string $name, string $role = 'student'): array {
return ['name' => $name, 'role' => $role];
}
$user1 = createUser('Иван'); // role = 'student'
$user2 = createUser('Анна', 'admin'); // role = 'admin'
Nullable-типы
Если аргумент или возврат может быть null:
<?php
function findUser(int $id): ?array {
// ?array = может вернуть array или null
$users = [1 => ['name' => 'Иван']];
return $users[$id] ?? null;
}
$user = findUser(99); // null
Вариативные аргументы (...$args)
<?php
function sum(int ...$numbers): int {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
echo sum(1, 2, 3, 4); // 10
Spread-оператор работает и при вызове:
<?php
$nums = [10, 20, 30];
echo sum(...$nums); // 60
Именованные аргументы (PHP 8+)
<?php
function createPost(string $title, string $body, bool $published = false): array {
return ['title' => $title, 'body' => $body, 'published' => $published];
}
// Можно указать только нужные, в любом порядке:
$post = createPost(
title: 'Привет',
published: true,
body: 'Текст поста',
);
strict_types
В начале файла:
<?php
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
echo add(2, 3); // 5
echo add('2', '3'); // TypeError!
Без strict_types PHP молча приведёт "2" к 2. С ним - выбросит TypeError (подробнее о типах - в уроке про переменные).
Анонимные функции (closures)
<?php
$multiply = function(int $a, int $b): int {
return $a * $b;
};
echo $multiply(3, 4); // 12
Анонимные функции часто передают в array_map, array_filter, usort:
<?php
$prices = [100, 250, 50, 400];
$expensive = array_filter($prices, function(int $price): bool {
return $price > 200;
});
// [250, 400]
use - захват переменных из внешней области
В PHP функции НЕ видят внешние переменные автоматически (в отличие от JavaScript):
<?php
$tax = 0.2;
$withTax = function(float $price) use ($tax): float {
return $price * (1 + $tax);
};
echo $withTax(100); // 120
Стрелочные функции (PHP 7.4+)
Короткий синтаксис для однострочных:
<?php
$double = fn(int $n): int => $n * 2;
echo $double(5); // 10
// В array_map:
$nums = [1, 2, 3];
$doubled = array_map(fn($n) => $n * 2, $nums);
// [2, 4, 6]
Стрелочные функции захватывают внешние переменные автоматически (без use):
<?php
$rate = 1.1;
$applyRate = fn(float $price) => $price * $rate;
echo $applyRate(100); // 110
Область видимости переменных
В PHP каждая функция имеет свою область видимости. Внешние переменные не доступны:
<?php
$name = 'Иван';
function sayName(): void {
// echo $name; // Warning: Undefined variable
// Нужно либо передать аргументом, либо use (для замыканий)
}
Глобальные переменные доступны через global (но это антипаттерн):
<?php
$counter = 0;
function increment(): void {
global $counter; // не делай так в реальном коде
$counter++;
}
Функции как аргументы (callable)
<?php
function applyToAll(array $items, callable $fn): array {
$result = [];
foreach ($items as $item) {
$result[] = $fn($item);
}
return $result;
}
$names = ['иван', 'анна', 'пётр'];
$upper = applyToAll($names, 'mb_strtoupper');
// ['ИВАН', 'АННА', 'ПЁТР']
// Или с анонимной функцией:
$lengths = applyToAll($names, fn($s) => mb_strlen($s));
// [4, 4, 4]
include vs require
Разница - в поведении при ошибке:
<?php
// include - если файла нет, будет warning, код продолжит выполняться
include __DIR__ . '/optional-helpers.php';
// require - если файла нет, fatal error (скрипт остановится)
require __DIR__ . '/helpers.php';
В реальных проектах почти всегда используй require - если файл нужен, его отсутствие должно быть ошибкой.
include_once / require_once
Гарантируют, что файл подключится только один раз:
<?php
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/config.php'; // второй вызов ничего не сделает
Это важно для файлов с определениями функций и классов - повторное подключение вызовет Cannot redeclare function.
Структура проекта с include
project/
├── index.php # точка входа
├── config.php # настройки
├── helpers.php # утилитарные функции
└── templates/
├── header.php # шапка HTML
└── footer.php # подвал HTML
<?php
// index.php
require __DIR__ . '/config.php';
require __DIR__ . '/helpers.php';
$title = 'Главная';
require __DIR__ . '/templates/header.php';
echo '<h1>Добро пожаловать!</h1>';
require __DIR__ . '/templates/footer.php';
Типичные ошибки
- Сигнатура без type hints.
function save($user, $data)принимает что угодно, баги вылезают в рантайме. Добавь типы аргументов и возврата:function save(User $user, array $data): void. В Symfony-контроллерах type hints обязательны - autowiring без них не сработает. - Функция без
returnтам, где он ожидается. Молча возвращаетnull, вызывающий код ловитTypeErrorна следующем шаге. Поставь: voidдля процедур и: ReturnTypeдля функций - IDE подсветит пропущенныйreturn. - Длинная сигнатура с 6+ параметрами.
createUser(string $name, string $email, int $age, string $role, bool $active, ?string $phone)- нарушение SRP и боль на вызывающей стороне. Сверни в DTO/readonly-класс или массив с типизированными ключами. - Позиционный вызов вместо named arguments.
createPost("Title", "Body", false, true, null)- никто не помнит, что значит четвёртыйtrue. ИспользуйcreatePost(title: ..., body: ..., published: true)- PHP 8 это поддерживает нативно. - Скрытые побочные эффекты. Функция
calculatePrice($items)внутри пишет в БД или мутирует$_SESSION- её нельзя протестировать и переиспользовать. Pure-логика отдельно, IO - отдельным сервисом, который явно инжектится.
Best practices
- Ставь type hints и return types на каждую функцию, включая
voidиnever. В Symfony это база - без типов не работает DI. - Используй union (
int|string), nullable (?T) иmixedточечно - это документация в сигнатуре, читается с одного взгляда. - Variadic (
...$args) применяй, когда количество аргументов реально переменное (array_merge-стиль), а не как замену массиву. - Named arguments - для функций с 3+ параметрами или булевыми флагами: вызов читается без подглядывания в сигнатуру.
- Держи функции pure: вход → выход, без обращения к глобалкам, файлам, БД. Сайд-эффекты - в отдельный слой (репозиторий, сервис).
Мини-задание
- Сделай
helpers.phpс функциейdd($value): never(var_dump + die) - Подключи его через
requireи вызовиdd(["test" => 123]) - Напиши функцию
sum(int ...$numbers): intс вариативными аргументами - Напиши анонимную функцию, которая фильтрует массив строк по минимальной длине (используй
use) - Перепиши её как стрелочную функцию (
fn() =>) - Добавь
declare(strict_types=1)и проверь, чтоsum("1", "2")вызывает ошибку