PHP: полный мануал с примерами, функциями и ООП
Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Дерево руководства
Глава 147 · От первого вызова к справочнику
Вызов функций и callback: полный справочник Function Handling
От выбора действия и передачи аргументов до диагностики вызова, наследования static-контекста, shutdown и ticks. Все 13 официальных функций, включая историческую create_function.
Сначала полезное действие: подпись заказа из одной функции
От вызова orderLabel(41) к строке на экране
Объявление сохраняет правило построения подписи. Его тело начинает работать при вызове внутри echo.
- 1echo orderLabel(41)↓
Чтобы вывести ответ, сначала нужно вычислить вызов.
- 2function orderLabel($number)↓
Аргумент 41 связывается с локальным параметром number.
- 3return «Заказ №» . $number↓
Тело строит строку «Заказ №41» и завершает вызов.
- 4Обратно в выражение echo↓
Место orderLabel(41) занимает возвращённая строка.
- 5Вывод: Заказ №41
echo печатает результат; дальнейших инструкций в первом примере нет.
Функция не печатает сама. Если сохранить результат в переменную, тот же return вернётся в правую часть присваивания.
Страница заказа и письмо должны показывать одинаковую подпись. Вынесем построение текста в функцию. Сперва вызовем её обычным способом; затем сможем передать выбор действия в другой участок программы. Здесь ничего не отправляется по сети: результат — строка в консоли.
- В месте
orderLabel(41)PHP выбирает объявление функции - Её параметр
$numberполучает число 41 - Точка соединяет «Заказ №» с 41;
returnвозвращает строку - Строка занимает место вызова;
echoпечатает «Заказ №41»
function начинает объявление. orderLabel — наше имя действия; параметр в круглых скобках — локальное имя входных данных. Фигурные скобки ограничивают тело. Оно выполняется при вызове, а не просто потому, что записано. return отдаёт результат вызывающему коду; точка соединяет текст. Точка с запятой завершает инструкцию. Если знак $ пока незнаком, начните с одного значения и переменной.
<?php
function orderLabel($number)
{
return 'Заказ №' . $number;
}
echo orderLabel(41);
Ожидаемый результат
Заказ №41Сохраните как example.php и выполните php example.php. Сама функция не печатает текст: его печатает echo в месте вызова. Меняя 41 на 52, получаем подпись другого заказа без копирования тела.
Callback: передать действие, чтобы вызвать его в выбранном месте
Callback — вызываемое действие, переданное как значение. Это не обещание асинхронности и не обязательно ответ внешнего сервера. Переменная может содержать имя функции или объект замыкания. Сохранение callback и его выполнение — разные шаги.
<?php
function orderLabel($number)
{
return 'Заказ №' . $number;
}
$formatter = 'orderLabel';
echo 'Действие выбрано', "\n";
echo $formatter(41);
Ожидаемый результат
Действие выбрано
Заказ №41Кавычки сохраняют имя как строку. Запись $formatter(41) вызывает выбранную функцию. Если написать orderLabel(41) при выборе, получится уже строка результата, а не само действие. Для одного заранее известного вызова обычная запись проще.
- Записать
$formatter = 'orderLabel': сохранить выбор call_user_func($formatter, 41): вызвать сейчасregister_shutdown_function($callback): зарегистрировать на завершение- Действие выполняет тот механизм, которому вы его передали; слово callback само время не задаёт
call_user_func и call_user_func_array выполняют callback сразу и возвращают его результат. register_shutdown_function сохраняет его до завершения скрипта. Не переносите одно поведение на другую функцию только из-за похожего аргумента.
В каких формах передают вызываемое действие
| Форма | Пример записи | Предусловие |
|---|---|---|
| Имя функции | 'orderLabel' | Функция объявлена и имя полно; строка не получает use-псевдоним автоматически. |
| Замыкание | function ($number) { return ...; } | Объект Closure с готовым телом; присваивание не запускает тело. |
| Метод объекта | [$client, 'listItems'] | Есть конкретный объект; метод доступен в месте вызова. |
| Статический метод | [Report::class, 'label'] | Метод действительно static; PHP 8 не делает обычный метод статическим. |
| Вызываемый объект | $formatter с __invoke | У объекта есть доступный метод __invoke. |
Сигнатура callable $callback означает, что аргумент должен быть вызываемым. Это не гарантирует подходящие параметры или отсутствие исключений внутри. Для строк и массивов важна текущая область доступа; function_exists не проверяет метод объекта. Языковые конструкции echo/isset/include не являются обычными callback-функциями. Чтобы передать печать, оберните echo в собственную функцию.
С PHP 8.1 запись $formatter = orderLabel(...); создаёт Closure для уже существующей функции: три точки здесь не запускают её и не передают данные. Форма $formatter(...$values), напротив, распаковывает аргументы и выполняет вызов. Различайте этот синтаксис с объявлением замыкания. Контекстные строковые callback вида 'parent::method' устарели с PHP 8.2; предпочитайте явное имя или first-class callable в разрешённой области.
Полный справочник методов Closure находится в отдельной официальной ветке предопределённых классов, а не в book.funchand. Эта глава не подменяет его: работу замыканий, захват состояния и области доступа продолжайте в главе «Замыкания».
Список аргументов — отдельный договор вызова
Для подписи достаточно номера, для строки отчёта понадобятся ещё формат и язык. Нужно понимать, по чему входные значения попадут в параметры: по позиции или по имени. Ассоциативный массив запроса не становится правильным списком вызова автоматически.
- Ввод проверен: номер 41, префикс «Заказ»
- Позиционный список
[41, 'Заказ']: первый элемент → number, второй → prefix - Именованный список
['prefix' => 'Заказ', 'number' => 41]: ключ → имя параметра - Тело возвращает одну строку; обёртка передаёт её в место вызова
С PHP 8 строковые ключи в call_user_func_array — именованные аргументы. Переименование параметра библиотеки может сломать такого клиента. Числовые ключи передаются по порядку обхода массива, а не по своему значению. Числовой аргумент после именованного даёт Error; неизвестное имя обычно тоже, кроме предусмотренного сбора в variadic-параметр.
Если задача требует позиционного вызова, сформируйте список в явном порядке. array_values снимает ключи, но не угадывает, какой параметр вы имели в виду: переставленный вход останется переставленным. Ссылочный параметр требует отдельного договора с &; обычный call_user_func не передаёт аргументы по ссылке.
Разберём новые обозначения до опыта: int перед $count задаёт тип входного параметра, : int после скобок — тип результата. declare(strict_types=1) включает строгую скалярную проверку для соответствующих вызовов из этого файла. try выполняет попытку, catch (TypeError $error) получает ошибку типа и позволяет продолжить код после обработанного отказа. Здесь строку специально передают вместо числа, чтобы увидеть обе границы вызова.
<?php
declare(strict_types=1);
function checkedCount(int $count): int { return $count; }
try {
checkedCount('3');
} catch (TypeError $error) {
echo "Прямой вызов отклонён\n";
}
try {
call_user_func('checkedCount', '3');
} catch (TypeError $error) {
echo "Callback отклонён\n";
}
Ожидаемый результат
Прямой вызов отклонён
Callback отклонён
Строка 3 не является int в строгом вызове. call_user_func здесь не обходит проверку. Если HTTP-форма передаёт текст, сначала проверьте его формат и допустимый диапазон, затем явно получите число; посредник не определяет правила бизнес-данных.
Для сопровождения универсальных обёрток есть дополнительный нюанс. В PHP 8.5.11 компилятор специально обрабатывает явный call_user_func(...) и array-вариант. Если в переменную положено имя самого посредника, используется другой путь внутреннего вызова; строгая проверка может смениться приведением. Это не полезный способ обходить типы, а причина не строить валидацию на предположении об универсальном callback-механизме.
<?php
declare(strict_types=1);
function checkedCount(int $count): int { return $count; }
$invoke = 'call_user_func';
$invokeArray = 'call_user_func_array';
echo $invoke('checkedCount', '3'), "\n";
echo $invokeArray('checkedCount', ['3']);
Ожидаемый результат
3
3Здесь переменная содержит не имя checkedCount, а имя внутреннего посредника. В проверенном PHP 8.5.11 числовая строка приводится к int на его внутреннем вызове. Прямой $callback(...) из строгого пользовательского файла проще выражает нужную границу. Это наблюдение реализации, а не рекомендация смешивать пути ради приведения.
Посмотреть существование, сигнатуру и реальные аргументы — разные задачи
| Нужно узнать | Инструмент | Что он не обещает |
|---|---|---|
| Объявлена ли функция с этим именем | function_exists | Не вызывает функцию и не проверяет доступ к внешнему сервису. |
| Какие функции уже доступны | get_defined_functions | Не находит ещё не подключённые файлы и не строит реестр безопасных действий. |
| Что передали текущей функции | func_get_arg / func_get_args / func_num_args | Не сообщает имена переменных вызывающего кода и не читает аргументы CLI. |
| Какие параметры объявлены | ReflectionFunction / ReflectionMethod | Структура объявления не равна истории всех вызовов. |
func_* вызывают внутри исследуемой пользовательской функции. Если вынести их в вспомогательную функцию, она увидит уже свои аргументы. Для консольных параметров есть $argv, для HTTP — данные запроса. Эти каналы не объединяются автоматически. Для нового API явные параметры и ...$items обычно понятнее скрытого разбора лишних аргументов.
Не отправляйте func_get_args целиком в журнал: там могут быть пароли, токены и персональные данные. Даже атрибут SensitiveParameter для stack trace не превращает обычный массив аргументов в безопасную запись. Выберите разрешённые поля: код события, безопасный идентификатор, количество элементов.
Завершение и ticks: когда именно PHP вызывает ваш код
- Основной код вызывает функции и получает результаты
- В участках
declare(ticks=N)дополнительные точки выполнения вызывают tick callback - Скрипт заканчивается или вызывает exit: выполняется очередь shutdown callback
- Процесс или среда запроса завершает оставшуюся очистку; принудительное убийство не даёт гарантии callback
Shutdown — всё ещё работа PHP этого запроса, не фоновая очередь. Долгая обработка удерживает ресурсы; отправка банковской команды на завершении не даёт надёжной доставки. Фиксацию нужного задания делают явным шагом в устойчивом хранилище, затем обрабатывают отдельным работником. В учебных примерах ниже только локальные строки и счётчики.
Tick — не секунда, не каждая строка исходника и не событие «вызвана функция». declare задаёт интервал в подходящих низкоуровневых инструкциях соответствующего участка исходника. Количество и распределение таких точек нельзя использовать как бизнес-счётчик; блокирующая операция не обязана уступить вам управление через тик. Для прогресса импорта вызывайте свою функцию после обработанной записи.
В PHP 8.5 tick-обработчики остаются активными дольше при завершении: до окончания shutdown-функций, деструкторов и очистки обработчиков вывода. Поэтому снимайте временный обработчик явно через unregister_tick_function; не рассчитывайте, что завершение само выключит его до вашей очистки.
Доступность, настройка и исторический код
Это встроенная область PHP: отдельный Composer-пакет или сетевой сервис для перечисленных функций не нужен. В данном официальном book нет самостоятельных страниц установки, INI-настроек или констант. Runtime относит часть функций к Core, часть к standard. Общие ограничения окружения, например disable_functions, могут менять доступность внутренних функций; это не отдельный набор настроек callback.
| Граница | Что учитывать |
|---|---|
| PHP 7.2 / 8.0 | create_function сначала deprecated, затем удалена; заменять Closure, не eval. |
| PHP 8.0 | Строковые ключи call_user_func_array стали именами параметров; отключённые внутренние функции убраны из таблицы. |
| PHP 8.2 | Контекстные формы callback parent::/self::/static:: в строках устарели. |
| PHP 8.5 | У get_defined_functions устарел только бесполезный exclude_disabled; сама функция остаётся доступна. |
| PHP 8.5 | Tick callbacks выключаются позднее при shutdown; не переносить старое предположение о порядке. |
Самостоятельные примеры проверены на PHP 8.5.11. Общие примеры рассчитаны на современный PHP 8.2+, а особые случаи помечены. Формулировки некоторых страниц func_* ещё описывают старые warning/false: текущие Error/ValueError ниже подтверждены runtime и исходниками движка. Это не обещание одинакового текста диагностики на всех версиях и SAPI.
Практика: выбрать формат результата по разрешённому имени
Сервис получает выбор формата, например compact или full. Не разрешаем внешней строке произвольно вызывать любую функцию процесса. Отдельный массив связывает два разрешённых слова с написанными нами обработчиками; данные заказа передаются уже после выбора. Для этого шага нужны ключи массива и условие if.
<?php
$formatters = [
'compact' => function ($order) { return '#' . $order['id']; },
'full' => function ($order) { return 'Заказ №' . $order['id'] . ': ' . $order['items'] . ' позиции'; },
];
$requested = 'full';
$order = ['id' => 41, 'items' => 3];
if (!isset($formatters[$requested])) {
throw new InvalidArgumentException('Неизвестный формат');
}
$text = $formatters[$requested]($order);
echo $text;
Ожидаемый результат
Заказ №41: 3 позицииКлюч formatters выбирает действие, скобки передают один массив order, return отдаёт строку в text. Данные фиксированы для запуска. В реальном запросе отдельно проверяются тип/значения полей и права на заказ; вставка результата в HTML требует экранирования.
Задание. Добавьте формат count, который вернёт «Позиций: 3». Сохраните запрет неизвестного формата. Затем заставьте один обработчик бросить исключение и решите явно: прекращать операцию или продолжать для других заказов.
Разбор
Добавьте в разрешённый массив ключ count с function ($order) { return 'Позиций: ' . $order['items']; }. Для единственного результата исключение можно передать вызывающему коду. Для пакетного экспорта у каждого заказа нужна явная запись успеха/отказа; молчаливое продолжение скрывает потерю данных. Callback сам не выбирает эту политику.
Таблица разрешённых действий делает замену одного обработчика понятной. Если у него появятся разные токены и настройки, следующим шагом станет клиент интеграции; если достаточно простой функции, объект не обязателен. Ни существование имени, ни is_callable не являются проверкой разрешений.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
call_user_funcВыполнить выбранное действие сейчас, передав аргументы отдельно.
PHP 4+ · standard
call_user_func(callable $callback, mixed ...$args): mixed| Параметр / результат | Что именно означает |
|---|---|
| callback | Функция, доступный метод, Closure или вызываемый объект. Неверный callback в PHP 8 даёт TypeError. |
| ...args | Ноль или более аргументов для callback; многоточие в сигнатуре обозначает переменное количество. По ссылке они не передаются. |
| mixed | Собственный результат callback; null для обычной функции без return, false может быть её допустимым результатом. |
| Отказы | Нехватка обязательных аргументов — ArgumentCountError, неподходящие типы — TypeError; исключения тела не проглатываются. |
Когда действие хранится в переменной, эта функция вызывает его прямо сейчас. Первым аргументом идёт само действие, затем его входные данные. Возвращаемое значение проходит наружу без автоматического вывода. Если функция известна заранее, orderLabel(41) короче; если готов callable, $callback(41) тоже поддерживается.
- Выбрать функцию по первому аргументу
- Передать 41 в её $number
- Выполнить тело и получить строку
- Вернуть эту строку вызывающему echo
<?php
function orderLabel($number) { return 'Заказ №' . $number; }
$text = call_user_func('orderLabel', 41);
echo $text;
Ожидаемый результат
Заказ №41Имя передаётся строкой; значение 41 относится к параметру orderLabel, а не к самому посреднику. Присваивание сохраняет результат, echo показывает его. Без return у callback обычный результат будет null.
Следующая ступень — объект с настройкой. public позволяет вызвать метод снаружи; свойство prefix принадлежит конкретному экземпляру. Массив из объекта и строки имени метода сохраняет именно этот экземпляр. Стрелка -> читает его состояние.
<?php
class ShopLabel
{
public $prefix;
public function __construct($prefix) { $this->prefix = $prefix; }
public function format($number) { return $this->prefix . ' #' . $number; }
}
$retail = new ShopLabel('Розница');
$wholesale = new ShopLabel('Опт');
echo call_user_func([$retail, 'format'], 41), "\n";
echo call_user_func([$wholesale, 'format'], 41);
Ожидаемый результат
Розница #41
Опт #41Оба callback вызывают одинаковый метод, но объекты несут разные настройки. new запускает конструктор; call_user_func запускает format. Передавать [ShopLabel::class, 'format'] нельзя: этот метод требует объект, он не static.
Параметры callback здесь не передаются по ссылке. Если он требует &$value, возникает E_WARNING; изменение не следует считать изменением исходной переменной. Для такой задачи прямой вызов переменной-функции либо array-вариант с явными ссылками имеет другой контракт. Возврат по ссылке также не сохраняется как внешнее ссылочное присваивание через этот посредник.
С PHP 8 дополнительные аргументы можно передавать по именам параметров callback, например call_user_func($formatter, number: 41). Это делает имя параметра частью договора: его переименование ломает такой вызов. Позиционные значения должны идти до именованных. Сам первый параметр посредника называется callback, поэтому для callback с одноимённым входным параметром удобнее прямой вызов либо явно собранный array-вариант, чтобы не спутать два уровня.
<?php
try {
call_user_func('missing_manual_formatter');
} catch (TypeError $error) {
echo "Действие недоступно\n";
}
$format = function ($number) { throw new RuntimeException('Формат временно недоступен'); };
try {
call_user_func($format, 41);
} catch (RuntimeException $error) {
echo "Ошибка обработчика\n";
}
Ожидаемый результат
Действие недоступно
Ошибка обработчика
TypeError относится к непригодному callback. RuntimeException выброшен его телом и прошёл через посредника. Последующие строки не выполнятся автоматически, если исключение не перехватить на выбранной границе.
Сравните: call_user_func_array, function_exists. Основы: вызов и возврат; передача по ссылке.
call_user_func_arrayВызвать действие с готовым массивом позиционных или именованных аргументов.
PHP 4.0.4+; именованные аргументы PHP 8.0+ · standard
call_user_func_array(callable $callback, array $args): mixed| Параметр / результат | Что именно означает |
|---|---|
| callback | Вызываемое действие; правила доступности те же, что у call_user_func. |
| args: array | Элементы с числовыми ключами идут позиционно в порядке обхода. Строковые ключи с PHP 8 — имена аргументов. |
| Порядок / совпадение | Позиционный после именованного, повторное заполнение параметра, неизвестное имя без variadic — Error; отсутствующий обязательный параметр — ArgumentCountError. |
| mixed / исключения | Возврат callback, включая законный false; ошибки и исключения вызова проходят наружу. Для ссылочного параметра нужен ссылочный элемент. |
Подходит, когда список аргументов уже собран, например в диспетчере локальных команд. Слово array относится к контейнеру аргументов: каждый элемент становится отдельным аргументом. Чтобы передать один массив как один параметр, нужна внешняя оболочка [$order], а не просто $order.
- Массив содержит два элемента
- 41 попадает в $number, «Заказ» — в $prefix
- makeLabel возвращает строку
- Результат возвращается из call_user_func_array
<?php
function makeLabel($number, $prefix) { return $prefix . ' №' . $number; }
$arguments = [41, 'Заказ'];
echo call_user_func_array('makeLabel', $arguments);
Ожидаемый результат
Заказ №41Числовые ключи списка не являются именами параметров. Передача [8 => 41, 2 => 'Заказ'] тоже использует порядок элементов 41, затем Заказ, а не сортировку ключей.
<?php
function makeLabel($number, $prefix = 'Заказ') { return $prefix . ' №' . $number; }
echo call_user_func_array('makeLabel', ['prefix' => 'Возврат', 'number' => 41]), "\n";
try {
call_user_func_array('makeLabel', ['id' => 41]);
} catch (Error $error) {
echo "Имя параметра не совпало\n";
}
Ожидаемый результат
Возврат №41
Имя параметра не совпало
PHP 8 связывает prefix и number по именам независимо от порядка этих двух ключей. id не объявлен и не собирается в variadic: вызов не состоялся. Это причина поломки старых маршрутизаторов, передававших произвольные ассоциативные поля.
При необходимости изменить исходный аргумент элемент args должен быть настоящей ссылкой. Амперсанд внутри массива создаёт связь; функция не угадывает её по имени поля. Эффект ниже полезен при сопровождении старого общего диспетчера, но для обычной сортировки предпочтительнее прямой sort($weights, SORT_NUMERIC).
<?php
$weights = [300, 100, 200];
$arguments = [&$weights, SORT_NUMERIC];
$ok = call_user_func_array('sort', $arguments);
var_dump($ok);
echo implode(',', $weights);
Ожидаемый результат
bool(true)
100,200,300Первый элемент args связан с weights. Возврат sort — true, сами отсортированные значения находятся в weights. Для callback без ссылочных параметров дополнительные & не нужны и могут неожиданно объединить данные.
Современная альтернатива — $callback(...$args), также с правилами позиционных/именованных аргументов. Проверяйте не только существование callback, но и договор данных. Не передавайте $_POST или JSON-объект напрямую как параметры: выберите разрешённые поля и порядок. В старом описании PHP.net встречается «false on error»; в PHP 8 неверный callback/неверное именованное связывание обычно бросают TypeError/Error, поэтому false нельзя использовать как универсальный детектор сбоя.
Сравните: call_user_func, func_get_args, forward_static_call_array. Основы: вызов и возврат; передача по ссылке.
register_shutdown_functionЗарегистрировать короткое действие на завершение текущего выполнения.
PHP 4+; изменение exit() при shutdown — PHP 8.4+ · standard
register_shutdown_function(callable $callback, mixed ...$args): void| Параметр / результат | Что именно означает |
|---|---|
| callback | Сохраняемое вызываемое действие. Выполнится при завершении, если выполнение дошло до регистрации и среда позволяет shutdown. |
| ...args | Значения для отложенного вызова; его контракт должен подходить. Это не список команд ОС. |
| void | Регистрация не возвращает результат будущего callback. Дополнительные регистрации дополняют очередь. |
| Порядок | Очередь по порядку регистрации; exit внутри callback останавливает дальнейшую очередь. Callback может добавить запись в её конец. |
Нужна для короткого заключительного действия в конце выполнения: например, показать локальную сводку импорта независимо от того, каким обычным путём он закончен. Регистрация не выполняет callback немедленно. PHP сохраняет его вместе с дополнительными аргументами и вызывает после окончания скрипта либо exit. Это не отдельный поток и не долговечная очередь заданий.
- register_shutdown_function сохраняет callback
- echo основного кода выполняется сейчас
- Основной код заканчивается
- PHP вызывает сохранённый callback с его аргументами
- Его return не возвращается к уже прошедшему месту регистрации
<?php
function finishReport($name) { echo 'Завершён: ', $name, "\n"; }
register_shutdown_function('finishReport', 'каталог');
echo "Основная работа\n";
Ожидаемый результат
Основная работа
Завершён: каталог
Скобки регистрации передают имя callback и аргумент каталог. Если написать finishReport('каталог') вместо строки, функция выполнится сразу, а регистрировать придётся её результат null — это ошибка. Результат callback не становится результатом register_shutdown_function.
В прикладном опыте нужно увидеть итог каждой строки. Замыкание с use (&$results) читает общее состояние на момент завершения; обычные значения аргументов регистрации не переснимаются автоматически после каждого изменения. & здесь выбран осознанно после главы ссылок.
Вместо нерасшифрованного счётчика покажем, что случилось с каждой строкой. Здесь правило заказа допускает только положительное целое quantity. Основной код заполняет results, а callback читает этот общий массив на завершении. Регистрация возвращает void; будущий отчёт получается через echo внутри callback, а не через присваивание результата регистрации.
<?php
$results = [];
register_shutdown_function(function () use (&$results) {
echo "Сводка проверки:\n";
foreach ($results as $line) { echo $line, "\n"; }
});
$rows = [
['sku' => 'TEA', 'quantity' => 2],
['sku' => 'CUP', 'quantity' => 0],
['sku' => 'COFFEE', 'quantity' => 3],
];
foreach ($rows as $row) {
if (!is_int($row['quantity']) || $row['quantity'] < 1) {
$results[] = $row['sku'] . ': отказ, количество должно быть положительным целым';
continue;
}
$results[] = $row['sku'] . ': принято ' . $row['quantity'] . ' шт.';
}
echo "Основной код закончил проверку\n";
Ожидаемый результат
Основной код закончил проверку
Сводка проверки:
TEA: принято 2 шт.
CUP: отказ, количество должно быть положительным целым
COFFEE: принято 3 шт.
При регистрации results ещё пуст, но use(&$results) сохраняет связь. Основной foreach добавляет три готовые строки: два принятия и отказ для нуля. Continue пропускает добавление успешной строки у CUP. После последнего echo заканчивается основной файл, PHP вызывает зарегистрированное замыкание, и оно видит заполненный results. Ни склад, ни база не меняются: «принято» означает пройдена именно эта проверка фиксированных учебных данных. Для реального импорта отдельно нужны сохранение результата и правила обработки сбоев.
<?php
register_shutdown_function(function () {
echo "Первая очистка\n";
register_shutdown_function(function () { echo "Добавленная очистка\n"; });
});
register_shutdown_function(function () { echo "Вторая очистка\n"; });
echo "Основной код\n";
exit;
Ожидаемый результат
Основной код
Первая очистка
Вторая очистка
Добавленная очистка
exit в основном коде запускает завершение. Новая регистрация из callback добавляется в конец. Если exit вызвать внутри первого callback, последующие callbacks не выполнятся. В рабочей очистке не нужно управлять всей очередью через exit.
Неверный callable в современной версии вызывает TypeError при регистрации; ошибки количества/типов дополнительных аргументов могут обнаружиться только при отложенном вызове. Исключение внутри callback само не превращается в успешное завершение; держите заключительную операцию короткой и обработайте ожидаемые отказы внутри неё. Публичной unregister_shutdown_function в этой семье нет: избегайте многократной регистрации из циклов. Для отменяемой собственной задачи можно хранить явный флаг, который callback проверяет.
Shutdown callbacks выполняются в рамках запроса и могут работать с буферами вывода; это не означает, что заголовки ещё разрешено отправлять. Некоторые серверы меняют текущий каталог к этой стадии: для файлов используйте заранее определённые абсолютные пути. Для обычного освобождения своего локального ресурса try/finally обычно показывает жизненный цикл точнее. Для наблюдения последней фатальной ошибки изучите error_get_last, но регистрация в неразобранном из-за синтаксической ошибки основном файле вообще не успеет выполниться.
Убийство процесса, SIGKILL/SIGTERM без обработанного нормального выхода, авария среды или внешнее ограничение службы могут не дать выполнить очередь. Нельзя обещать обязательную отправку письма или платежа. По документации стандартный max_execution_time не отсчитывает shutdown-функции как основной код; это не отменяет таймаутов FPM/веб-сервера и ограничений памяти. Не используйте это свойство для бесконечной работы.
Сравните: call_user_func, register_tick_function. Основы: вызов и возврат; передача по ссылке.
register_tick_functionПодключить наблюдатель к tick-точкам отмеченного участка PHP.
PHP 4.0.3+; позднее отключение handlers — PHP 8.5+ · standard
register_tick_function(callable $callback, mixed ...$args): bool| Параметр / результат | Что именно означает |
|---|---|
| callback | Вызываемое действие при возникновении tick; само добавление не задаёт tick-точки. |
| ...args | Аргументы, передаваемые наблюдателю при каждом вызове. |
| bool | true при успешной регистрации; документированный тип bool. Неверный callable в PHP 8 — TypeError, не универсальный false. |
| Связанный синтаксис | declare(ticks=N), где N — положительный интервал tickable-инструкций; это не миллисекунды и не номер строки. |
Позволяет подключить короткое наблюдение к tick-точкам PHP. Например, при локальном исследовании старого алгоритма нужно убедиться, что обработчик вообще получает управление. Для обычного приложения, прогресса заказа или ограничения запросов это обычно лишний механизм: явный вызов после обработанной строки яснее и предсказуемее.
- register_tick_function сохраняет callback
- declare(ticks=1) включает точки вызова в данном участке исходника
- После подходящей инструкции PHP вызывает callback
- unregister_tick_function снимает регистрацию
- Один declare без регистрации и одна регистрация без tick-точек не выполняют наблюдение
declare(ticks=1) { ... } задаёт режим компиляции ограниченного блока. Это не таймер на одну секунду. В первом примере callback сохраняет текущее число обработанных документов. Показываем сами данные, которые он успел увидеть; это число документов задаётся телом программы и не является количеством ticks.
<?php
$processed = 0;
$lastObserved = null;
$observer = function () use (&$processed, &$lastObserved) {
$lastObserved = $processed;
};
$registered = register_tick_function($observer);
try {
declare(ticks=1) {
$processed = 1;
$processed += 1;
}
} finally {
unregister_tick_function($observer);
}
if ($registered) { echo "Наблюдатель был зарегистрирован\n"; }
echo 'Обработано документов: ', $processed, "\n";
echo 'Последнее наблюдение: ', $lastObserved, "\n";
Ожидаемый результат
Наблюдатель был зарегистрирован
Обработано документов: 2
Последнее наблюдение: 2
Register_tick_function получает сам Closure и возвращает true в registered. Declare создаёт tick-точки для тела, где processed становится 1, затем 2. Callback читает processed и сохраняет в lastObserved; его собственный return не используется. После finally наблюдение снято; вывод показывает реальное последнее значение 2. Это учебные отметки документов, а не чтение их файлов. Количество событий может отличаться от количества присваиваний; пример не превращает ticks в бизнес-счётчик.
<?php
$result = [];
$seen = [];
$observer = function ($stage) use (&$result, &$seen) {
$seen[$stage] = $result;
};
register_tick_function($observer, 'Подготовка названий');
try {
declare(ticks=1) {
foreach ([' Tea ', ' Cup '] as $name) {
$result[] = trim($name);
}
}
} finally {
unregister_tick_function($observer);
}
foreach ($seen as $stage => $names) {
echo $stage, ': ', implode(', ', $names), "\n";
}
Ожидаемый результат
Подготовка названий: Tea, Cup
Второй аргумент регистрации — строка, которую PHP подставляет в местный stage каждого вызова observer. Use передаёт связи с result и seen отдельно от этого параметра. Trim возвращает Tea/Cup без крайних пробелов, тело добавляет их в result. Callback обновляет снимок списка для подписи участка; после последней точки он содержит оба названия. Foreach читает этот сохранённый массив и печатает его. В примере сохраняется только последнее наблюдение, поэтому не обещается запись каждого промежуточного состояния или наблюдение всего приложения.
Область declare задаётся исходником: включённый файл и тело функции, скомпилированное вне неё, не становятся автоматически полностью инструментированными. Не всякая низкоуровневая операция tickable. Этот механизм не умеет прервать зависший сетевой вызов в любой момент; задавайте таймаут операции на уровне её клиента. Не добавляйте sleep в tick callback для «вызова раз в секунду»: это замедлит работу без надёжного расписания.
Недопустимый callback вызывает TypeError; неверные аргументы и исключение callback обнаруживаются при вызове. Для отмены передайте ту же функцию/тот же объект callback в unregister_tick_function. Регистрация удерживает нужные ей объекты: в длительном выполнении снимайте временных наблюдателей и захваченные крупные данные. Частые callbacks увеличивают объём работы; для измерения производительности лучше профилировщик либо явные замеры всей полезной операции.
Сравните: unregister_tick_function, register_shutdown_function. Основы: вызов и возврат; передача по ссылке.
unregister_tick_functionЗавершить наблюдение тем же callback, который был зарегистрирован.
PHP 4.0.3+ · standard
unregister_tick_function(callable $callback): void| Параметр / результат | Что именно означает |
|---|---|
| callback | Та же вызываемая функция/метод или тот же объект Closure. Новый Closure с одинаковым телом не является прежним. |
| void | Полезное значение не возвращается; это не bool-проверка, был ли callback зарегистрирован. |
| Ошибки | Недопустимый callable — TypeError. Не путать со снятием shutdown: публичной парной функции для него нет. |
Снимает ранее подключённый tick callback. Это нужно после завершения локальной диагностики, чтобы наблюдатель не продолжал влиять на чужой код. Снятие и уничтожение всех функций — разные вещи: callback остаётся обычным значением переменной, но больше не вызывается по этой регистрации.
- Observer сохраняет текущее имя документа.
- Первый tick-участок устанавливает №42, и снимок получает №42.
- Unregister снимает именно сохранённый observer.
- Второй участок устанавливает №43; снимок по-прежнему №42.
<?php
$currentDocument = 'Заказ №41';
$lastObserved = null;
$observer = function () use (&$currentDocument, &$lastObserved) {
$lastObserved = $currentDocument;
};
register_tick_function($observer);
declare(ticks=1) { $currentDocument = 'Заказ №42'; }
unregister_tick_function($observer);
declare(ticks=1) { $currentDocument = 'Заказ №43'; }
echo 'Последнее наблюдение: ', $lastObserved, "\n";
echo 'Текущий документ: ', $currentDocument, "\n";
Ожидаемый результат
Последнее наблюдение: Заказ №42
Текущий документ: Заказ №43
Первый участок даёт callback возможность сохранить №42. Unregister_tick_function получает тот же объект observer и снимает регистрацию. Второй участок меняет currentDocument на №43, но lastObserved остаётся №42. Возврат unregister — void: результат действия читается в состоянии программы, а не как bool. Новый Closure с таким же текстом был бы другим объектом и не обозначал бы прежнюю регистрацию.
<?php
$stage = 'Ожидание';
$lastObserved = null;
$observer = function () use (&$stage, &$lastObserved) { $lastObserved = $stage; };
register_tick_function($observer);
try {
declare(ticks=1) {
$stage = 'Пакет получен';
throw new RuntimeException('Нет правила доставки');
}
} catch (RuntimeException) {
echo "Пакет отклонён: нет правила доставки\n";
} finally {
unregister_tick_function($observer);
}
declare(ticks=1) { $stage = 'Следующее задание'; }
echo 'Последнее наблюдение старого задания: ', $lastObserved, "\n";
echo 'Текущий этап: ', $stage, "\n";
Ожидаемый результат
Пакет отклонён: нет правила доставки
Последнее наблюдение старого задания: Пакет получен
Текущий этап: Следующее задание
Первое присваивание даёт наблюдение «Пакет получен». Throw передаёт управление обработчику отказа; finally снимает observer даже на этом пути. Затем stage меняется, но старый lastObserved не получает «Следующее задание». Удобство finally в том, что снятие не нужно дублировать в каждой успешной и ошибочной ветке. Это обычный обработанный сценарий исключения; убийство процесса или exit не дают такой же гарантии исполнения finally.
Возврата с количеством удалённых регистраций нет. Валидный, но незарегистрированный callable не нужно использовать как тест наличия: функция не возвращает такой признак. Не существует общего API выгрузки всех tick-регистраций в этой книге. Храните callback и ответственность за его снятие у того кода, который его добавил. В старых обсуждениях встречается ограничение снятия Closure; текущий контракт принимает callable и оба примера проверены на PHP 8.5.11.
Сравните: register_tick_function, register_shutdown_function. Основы: вызов и возврат; передача по ссылке.
create_functionРаспознать удалённое создание функции из строк и безопасно перенести его на Closure.
PHP 4.0.1–7.x; deprecated 7.2; удалена 8.0 · standard
Исторически: create_function(string $args, string $code): string|false| Параметр / результат | Что именно означает |
|---|---|
| args: string | Текст объявления параметров старой динамической функции; это не массив входных значений. |
| code: string | PHP-тело для динамической компиляции; подстановка данных в него могла стать исполнением чужого кода. |
| Исторический результат | Сгенерированное имя функции или false; это не объект Closure. |
| Современный PHP | Функция удалена: обычный вызов вызывает Error. Используйте анонимную функцию, именованную функцию или настроенный callable. |
В старом модуле можно встретить функцию, которая собирает PHP-код строкой и получает имя созданной функции. Так раньше имитировали анонимные функции. Сегодня сначала распознайте историческую запись, затем перенесите логику в обычный Closure: данные должны оставаться данными, а тело программы — исходным кодом.
<?php
// Исторический фрагмент для чтения; НЕ запускать в PHP 8.
$format = create_function('$number', 'return "Заказ №" . $number;');
Первой строкой описывались параметры, второй — исполняемое тело. В современном PHP такого имени функции нет: вызов даст Error. Не заменяйте отсутствие функции собственным eval-полифиллом. Этот исторический фрагмент не исполняется.
- Было: строка параметров и строка тела
- Стало: function ($number) { return ...; } в исходнике
- Переменная $format хранит объект Closure
- $format(41) запускает его и возвращает строку
<?php
$format = function ($number) {
return 'Заказ №' . $number;
};
echo $format(41);
Ожидаемый результат
Заказ №41Внешние скобки function описывают параметры, фигурные — тело. Строка Заказ № является обычным текстом результата. Для вызова используется переменная и скобки; никакой дополнительный компилятор строк не нужен.
Следующая практическая задача — разные подписи магазина. Не вставляем префикс в строку PHP-кода. Фабрика возвращает Closure, захвативший значение prefix через use. Каждый вызов фабрики создаёт отдельный объект с собственной настройкой.
<?php
function makeOrderFormatter($prefix)
{
return function ($number) use ($prefix) {
return $prefix . ' #' . $number;
};
}
$retail = makeOrderFormatter('Розница');
$wholesale = makeOrderFormatter('Опт');
echo $retail(41), "\n";
echo $wholesale(41);
Ожидаемый результат
Розница #41
Опт #41use сохраняет значение для будущего вызова; return внешней функции отдаёт действие, а return внутренней — текст. Тело Closure не обязано быть static или методом класса. Объекты-клиенты нужны позже, когда настройки сопровождаются несколькими связанными операциями.
Исторически args содержал текст списка параметров, code — текст тела; функция возвращала сгенерированное имя либо false при неудаче. Имя могло содержать нулевой байт и не было хорошим внешним идентификатором. Код компилировался динамически; подстановка непроверенных данных позволяла выполнить чужую программу. Создаваемые глобальные функции не освобождались как обычные временные Closure, поэтому циклическое создание также расходовало ресурсы. Это описание риска старой реализации, а не способ рекомендованной оптимизации.
При миграции найдите все места создания и последующего вызова. Заменяйте строку параметров настоящими параметрами, внедрённые значения — use или явными аргументами. Если callback сохранялся в базе как сгенерированное имя, новый Closure нельзя считать совместимым сериализуемым идентификатором: храните разрешённое имя операции и восстанавливайте обработчик из реестра. Конкретный выбор и состояние подробно разобраны в замыканиях.
Сравните: function_exists, call_user_func. Основы: вызов и возврат; передача по ссылке.
forward_static_callВызвать выбранный метод предка, сохранив подходящий дочерний static-контекст.
PHP 5.3+ · standard
forward_static_call(callable $callback, mixed ...$args): mixed| Параметр / результат | Что именно означает |
|---|---|
| callback | Функция либо доступный метод; практическая особенность относится к forwarding static-контекста метода. |
| ...args | Отдельные позиционные значения callback; не по ссылке. Дополнительные named-аргументы в PHP 8.5.11 дают ArgumentCountError. |
| Область вызова | Нужен активный контекст метода класса. В глобальной области — Error. |
| mixed | Собственный результат callback, без автоматического echo. TypeError/Error и исключения тела проходят наружу. |
Нужна в специализированной статической обёртке: метод предка выбран как callback, но внутри него static:: должен продолжить видеть вызвавший дочерний класс. Это тема после self, parent и позднего связывания. Если родительский метод известен заранее, parent::method(...) обычно выражает намерение проще.
Сначала разделим два понятия. Объявляющий класс определяет, где написано тело метода. Вызываемый класс для static:: показывает, из какой совместимой ветки наследования пришёл вызов. Явный обычный callback на базовый класс может установить базовый контекст; forwarding-вызов сохраняет дочерний для вызова совместимого метода предка.
- Invoice::compare(41) начинает работу в Invoice
- Callback указывает на тело Document::label
- Обычный call_user_func использует Document как called class
- forward_static_call передаёт подходящий текущий called class Invoice
- Тот же static::PREFIX читает разную константу
<?php
class Document
{
public const PREFIX = 'Документ';
public static function label($number) { return static::PREFIX . ' #' . $number; }
}
class Invoice extends Document
{
public const PREFIX = 'Счёт';
public static function compare($number)
{
echo call_user_func([Document::class, 'label'], $number), "\n";
echo forward_static_call([Document::class, 'label'], $number);
}
}
Invoice::compare(41);
Ожидаемый результат
Документ #41
Счёт #41extends задаёт наследование, static объявляет метод без экземпляра, :: выбирает член класса, ::class даёт строку его имени. static::PREFIX внутри унаследованного тела использует called class. Этот опыт нужен для понимания механизма; обычному форматтеру достаточно функции с явным префиксом.
<?php
class ExportDocument
{
public const KIND = 'document';
public static function row($number, $amount)
{
return static::KIND . ':' . $number . ':' . $amount;
}
public static function export($number, $amount)
{
$formatter = [self::class, 'row'];
return forward_static_call($formatter, $number, $amount);
}
}
class InvoiceExport extends ExportDocument { public const KIND = 'invoice'; }
class RefundExport extends ExportDocument { public const KIND = 'refund'; }
echo InvoiceExport::export(41, 1200), "\n";
echo RefundExport::export(41, 300);
Ожидаемый результат
invoice:41:1200
refund:41:300export объявлен один раз. В каждом вызове выбирается тело row предка, но static::KIND остаётся связан с нужным видом документа. В этом фиксированном примере self::row($number, $amount) проще; посредник имеет смысл, когда callback действительно выбирается обёрткой. Он не выполняет поиск переопределённого тела вместо явно указанного row.
Функцию необходимо вызывать из контекста метода класса: глобальный вызов даёт Error. Неверный callback даёт TypeError, а исключение тела проходит наружу. Сохранение контекста не означает, что любой несвязанный класс станет текущим: для callback другого, не совместимого с текущим called class класса используется его собственный контекст. Обычная функция-callback также допустима внутри метода, но у неё самой нет static:: класса.
Аргументы передаются отдельно, не по ссылке. В отличие от call_user_func, передача дополнительного именованного аргумента вроде prefix: 'Счёт' здесь на PHP 8.5.11 вызывает ArgumentCountError: посредник не принимает неизвестные ему именованные параметры для пересылки. Используйте позиционные значения; возможности array-варианта изучайте отдельно. Возвращается результат самого callback. false может быть нормальным результатом, а современные ошибки не следует сводить к старой общей формулировке «false при ошибке». Не принимайте имя класса или метода напрямую из запроса: корректный callable не равен разрешённой операции.
Сравните: forward_static_call_array, call_user_func. Основы: вызов и возврат; передача по ссылке.
forward_static_call_arrayПередать готовый список параметров с возможностью forwarding static-контекста.
PHP 5.3+; поведение строковых ключей проверяйте по ветке · standard
forward_static_call_array(callable $callback, array $args): mixed| Параметр / результат | Что именно означает |
|---|---|
| callback | Доступное вызываемое действие. Для метода предка возможно сохранение подходящего called class. |
| args | Массив параметров: для переносимости 8.2+ в опытах используется позиционный список; для передачи по ссылке нужен ссылочный элемент. |
| Контекст | В отличие от forward_static_call, активный метод класса не обязателен. |
| mixed / отказ | Возврат callback; современные TypeError/Error и исключения тела не превращаются в гарантированный false. |
Решает ту же задачу forwarding-вызова, когда аргументы уже собраны в массив. Для первого опыта достаточно увидеть: один массив превращается в два параметра обычной функции. В отличие от варианта без суффикса array, здесь допустим вызов в глобальной области — этот важный нюанс прямо отражён в текущей документации.
<?php
function lineLabel($number, $quantity) { return '#' . $number . ': ' . $quantity; }
$arguments = [41, 3];
echo forward_static_call_array('lineLabel', $arguments);
Ожидаемый результат
#41: 3Элемент 41 попадает в number, элемент 3 — в quantity. Функция lineLabel не использует static-контекст. Здесь call_user_func_array либо прямой lineLabel(...$arguments) понятнее; опыт показывает точную границу допустимого вызова, а не обязательную рекомендацию.
<?php
class ExportTemplate
{
public const LABEL = 'Документ';
public static function render($number, $quantity)
{
return static::LABEL . ' #' . $number . ': ' . $quantity;
}
public static function fromRow($row)
{
$arguments = [$row['number'], $row['quantity']];
return forward_static_call_array([self::class, 'render'], $arguments);
}
}
class InvoiceTemplate extends ExportTemplate { public const LABEL = 'Счёт'; }
echo InvoiceTemplate::fromRow(['number' => 41, 'quantity' => 3]);
Ожидаемый результат
Счёт #41: 3Входная запись остаётся данными, а fromRow выбирает явный порядок аргументов. Выбрано тело render из ExportTemplate, static::LABEL читает значение InvoiceTemplate. Вход в реальном приложении должен быть проверен до fromRow; отсутствие ключа не заменяется автоматическим значением.
<?php
function normalizeLabels(&$labels)
{
foreach ($labels as $key => $label) { $labels[$key] = trim($label); }
return count($labels);
}
$labels = [' Tea ', ' Cup '];
$arguments = [&$labels];
echo forward_static_call_array('normalizeLabels', $arguments), "\n";
echo implode(',', $labels);
Ожидаемый результат
2
Tea,CupВнешний args содержит один ссылочный элемент: он соответствует одному параметру labels. Передача массива labels без внешней оболочки превратила бы его строки в отдельные аргументы. В обычном коде прямой normalizeLabels($labels) лучше показывает изменение.
На современном runtime именованный ключ требует соответствующего имени либо variadic-приёмника. Позиционный элемент после именованного и повторное заполнение одного параметра приводят к Error. Для переноса между ветками сформируйте позиционный список по договору, а не снимайте неизвестные ключи наугад. Пример row выше специально перечисляет number, затем quantity.
При вызове совместимого метода предка из метода сохраняется текущий called class; глобальный вызов не создаёт дочерний класс из ничего. Неверный callable вызывает TypeError, неверное связывание аргументов — Error, ошибка внутри callback выходит наружу. Результат false сам по себе не означает сбой посредника. Для обычной маршрутизации действий forwarding почти всегда не нужен: выбирайте call_user_func_array или прямой callable.
Сравните: forward_static_call, call_user_func_array. Основы: вызов и возврат; передача по ссылке.
func_get_argПолучить один аргумент текущего вызова по позиции, начиная с нуля.
PHP 4+; текущие by-value значения 7.0+; именованные аргументы 8.0+ · Core
func_get_arg(int $position): mixed| Параметр / результат | Что именно означает |
|---|---|
| position: int | Номер от 0 до func_num_args() - 1 для текущего списка аргументов. Отрицательная/недоступная позиция — ValueError в PHP 8.5.11. |
| mixed | Текущее значение аргумента; законны null и false. Это не признак наличия и не возврат по ссылке. |
| Контекст | Только внутри пользовательской функции/метода/замыкания; глобальная область — Error. Вспомогательная функция увидит свой список. |
| Именованные вызовы | Позиции нормализованы к объявлению. Неизвестные именованные variadic-аргументы доступны через сам variadic-параметр. |
Позволяет прочитать значение по позиции из списка аргументов текущей пользовательской функции. Позиции начинаются с нуля: 0 — первый, 1 — второй. Это инструмент старых универсальных обёрток и диагностики; если у параметра уже есть понятное имя, чтение $quantity проще, чем func_get_arg(1).
Здесь внешний вызов передаёт номер заказа 41 и количество 3. Внутренний вызов func_get_arg(1) получает совсем другой аргумент: число позиции 1, по которой нужно прочитать количество текущего вызова. Не смешивайте эти два уровня параметров.
<?php
function orderLine($number, $quantity) {
$readNumber = func_get_arg(0);
$readQuantity = func_get_arg(1);
return 'Заказ №' . $readNumber . ': ' . $readQuantity . ' шт.';
}
$label = orderLine(41, 3);
echo $label, "\n";
Ожидаемый результат
Заказ №41: 3 шт.
Func_get_arg(0) возвращает 41 в readNumber, func_get_arg(1) — 3 в readQuantity. Внешний return соединяет значения со строками и отдаёт готовую подпись в label. Echo вызывающего показывает результат. Func_get_arg не получает номер заказа напрямую и не читает аргументы CLI. В этом простом объявлении прямые number и quantity яснее; чтение по позициям нужно прежде всего при сопровождении старого универсального интерфейса.
В старом интерфейсе часто принимали переменное число аргументов без явного ... . Сохраняя такой интерфейс при миграции, сначала проверяют количество, а уже затем читают необязательную позицию. Следующий опыт принимает только локальные проверенные значения; в новом коде лучше объявить $prefix явно.
<?php
function legacyLabel($number)
{
$prefix = func_num_args() >= 2 ? func_get_arg(1) : 'Заказ';
return $prefix . ' #' . $number;
}
echo legacyLabel(41), "\n";
echo legacyLabel(41, 'Возврат');
Ожидаемый результат
Заказ #41
Возврат #41Пользовательская функция может получить лишний позиционный аргумент, даже если он не указан в объявлении. Проверка защищает чтение второй позиции. Это пример поддержки legacy-договора, а не рекомендация скрывать новые параметры: function label($number, $prefix = 'Заказ') понятнее читателю и анализатору.
<?php
function inspectLine($number)
{
foreach ([-1, 1] as $position) {
try {
func_get_arg($position);
} catch (ValueError $error) {
echo 'Недоступная позиция: ', $position, "\n";
}
}
}
inspectLine(41);
Ожидаемый результат
Недоступная позиция: -1
Недоступная позиция: 1
inspectLine вызван с единственным аргументом: доступна только позиция 0. На PHP 8.5.11 отрицательная и выходящая за список позиции дают ValueError. Пример ловит конкретную ожидаемую ошибку, не скрывая остальные отказы.
В современном PHP вне пользовательской функции возникает Error. Старое описание warning/false на странице руководства не соответствует проверенному PHP 8.5.11; не пишите проверку func_get_arg(...) === false для определения неверной позиции — false может быть законным аргументом. Неверный тип position подчиняется обычной проверке int: непригодный тип даёт TypeError; строгий и приводящий режимы различаются.
С PHP 7 возвращается текущее значение параметра, в том числе после его локального изменения при передаче по значению. Это не неизменяемый протокол входящего запроса. С PHP 8 именованные аргументы рассматриваются в порядке объявления; пропущенные перед переданным именованным параметром заполняются defaults. Неизвестные именованные аргументы, собранные ...$extra, здесь не видны — их читайте в extra. Подробнее два опыта в func_get_args. Возвращаемое значение не является ссылкой на параметр; вложенный объект при этом остаётся тем же объектом, если его не клонировать.
Сравните: func_get_args, func_num_args. Основы: вызов и возврат; передача по ссылке.
func_get_argsПолучить нормализованный список аргументов текущей функции.
PHP 4+; текущие значения 7.0+; named 8.0+ · Core
func_get_args(): array| Параметр / результат | Что именно означает |
|---|---|
| Параметры | Нет. Вызывается в той пользовательской функции, чей список требуется прочитать. |
| array | Числовой список текущих значений. Не таблица имён параметров, не argv и не автоматический снимок всех defaults. |
| Версии | С PHP 7 изменения by-value параметра отражаются в результате. С PHP 8 именованные вызовы нормализуются к порядку объявления. |
| Ошибки / границы | В PHP 8.5.11 глобальный вызов — Error. Unknown named variadic читаются через ...$extra; копия не клонирует объекты. |
Возвращает список аргументов текущего вызова как массив с числовыми позициями. Так устроены многие старые обёртки, принимающие любое число значений и передающие их дальше. Сначала посмотрим массив, затем сравним его с современным явным ...$parts. Возврат массива не вызывает переданные значения как функции.
- Вызов передаёт два значения
- Текущий frame функции хранит её аргументы
- func_get_args() создаёт массив [41, 3]
- foreach читает позиции 0 и 1
- Это значения вызова, не имена $number и $quantity
<?php
function showArguments($number, $quantity)
{
$arguments = func_get_args();
foreach ($arguments as $position => $value) {
echo $position, ': ', $value, "\n";
}
}
showArguments(41, 3);
Ожидаемый результат
0: 41
1: 3
Параметров у func_get_args нет. foreach получает индекс и значение массива. Имена переменных вызывающего кода здесь не восстанавливаются; если нужен именованный договор данных, передайте явный массив с разрешёнными полями.
<?php
function joinLabelParts(...$parts) { return implode(' / ', $parts); }
function oldReportLabel()
{
return call_user_func_array('joinLabelParts', func_get_args());
}
function reportLabel(...$parts)
{
return joinLabelParts(...$parts);
}
echo oldReportLabel('Заказ', '41', 'Готов'), "\n";
echo reportLabel('Заказ', '41', 'Готов');
Ожидаемый результат
Заказ / 41 / Готов
Заказ / 41 / ГотовВ объявлении ...$parts собирает аргументы; в вызове ...$parts распаковывает их. Первый способ помогает прочитать старый код, второй показывает договор сразу. Этот опыт содержит только скалярные строки; нельзя бездумно переслать так параметры по ссылке и обещать сохранение их ссылочного договора.
<?php
function normalizeLabel($label)
{
$before = func_get_args();
$label = trim($label);
$after = func_get_args();
echo '[', $before[0], ']', "\n";
echo '[', $after[0], ']';
}
normalizeLabel(' Tea ');
Ожидаемый результат
[ Tea ]
[Tea]Снимок скалярного значения before сделан до изменения. После trim у текущего параметра уже Tea, и func_get_args показывает Tea. Для объектов такая копия массива не создаёт независимую глубокую копию их состояния; не называйте её универсальным журналом исходного ввода.
Именованные аргументы не превращают результат func_get_args в ассоциативный массив. Сначала ниже покажем конечные настройки, затем каждый элемент действительно возвращённого списка и отдельный variadic. Это отвечает на практическую путаницу из вопроса об именах в func_get_args.
<?php
function exportOptions($format = 'csv', $language = 'ru', ...$extra) {
echo 'Настройки: ', $format, '/', $language, "\n";
$arguments = func_get_args();
if ($arguments === []) { echo "Обычных переданных позиций нет\n"; }
foreach ($arguments as $position => $value) {
echo 'Позиция ', $position, ': ', $value, "\n";
}
foreach ($extra as $name => $enabled) {
echo 'Дополнительно ', $name, ': ', $enabled ? 'включено' : 'выключено', "\n";
}
}
exportOptions(language: 'en', trace: true);
exportOptions(trace: true);
Ожидаемый результат
Настройки: csv/en
Позиция 0: csv
Позиция 1: en
Дополнительно trace: включено
Настройки: csv/ru
Обычных переданных позиций нет
Дополнительно trace: включено
Первый вызов задаёт language во второй объявленной позиции: func_get_args возвращает [csv,en], включая предыдущий default. Неизвестное имя trace собрано в extra и в этот список не входит. Во втором вызове нет обычных переданных позиций, массив пуст, хотя тело использует оба defaults csv/ru. Дополнительные поля этого примера — только булевы флаги; для произвольных внешних значений нужна отдельная схема. Позиции массива не восстанавливают исходную запись вызова.
При обычном позиционном вызове пропущенные необязательные параметры в конец массива не добавляются. Именованный вызов имеет описанное выше заполнение до переданной позиции; поэтому по массиву нельзя восстановить точную запись исходного вызова. Набор неизвестных именованных variadic-аргументов отдельно от списка func_*. Вне пользовательской функции на PHP 8.5.11 будет Error, а не пригодный к обходу пустой массив.
Создаётся контейнер всех подходящих аргументов. Если нужен один параметр, используйте его имя; не собирайте большой массив ради одной проверки. Значения копируются по обычным правилам PHP, без рекурсивного клонирования объектов. Не превращайте массив в необработанный диагностический лог: он может содержать пароль, сессию или банковскую команду. Для диагностики выбирайте безопасные идентификаторы и количества, для нового публичного API — явные параметры/variadic.
Сравните: func_get_arg, func_num_args, call_user_func_array. Основы: вызов и возврат; передача по ссылке.
func_num_argsУзнать число позиций текущего вызова, отличая отсутствие аргумента от default.
PHP 4+; нормализация named PHP 8.0+ · Core
func_num_args(): int| Параметр / результат | Что именно означает |
|---|---|
| Параметры | Нет; работает внутри пользовательской функции, метода либо замыкания. |
| int | Количество аргументов текущего нормализованного списка, а не число объявленных параметров. |
| Named с PHP 8 | Пропущенные предыдущие позиции заполняются defaults; unknown named variadic не входят в число. Позиционные дополнительные входят. |
| Ошибка контекста | Глобальная область на PHP 8.5.11 — Error. Для аргументов CLI используйте argc/argv. |
Считает аргументы текущего вызова пользовательской функции. Это не количество параметров, написанных в её объявлении. Возможность нужна, например, старому setter/getter-интерфейсу: вызов без аргумента читает состояние, а явный null означает записать null. В новом API отдельные методы get/set обычно яснее, но при поддержке старого различие важно.
<?php
function packingLabel($quantity = 1): string {
if (func_num_args() === 0) {
return 'Количество не указано: используем ' . $quantity . ' упаковку';
}
return 'Выбрано упаковок: ' . $quantity;
}
echo packingLabel(), "\n";
echo packingLabel(1), "\n";
echo packingLabel(3), "\n";
Ожидаемый результат
Количество не указано: используем 1 упаковку
Выбрано упаковок: 1
Выбрано упаковок: 3
Первый вызов не передал аргумент: quantity получает default 1, func_num_args возвращает 0 и выбирает специальную подпись. Второй явно передал 1: количество аргументов равно 1, хотя quantity совпадает с default. Третий тоже передал один аргумент — число 3, поэтому счётчик равен 1, а текст количества равен 3. Return каждого вызова попадает в соответствующий echo. Это уже проверенные положительные целые; пример разбирает происхождение настройки, не валидацию формы.
<?php
function requestLabel($newLabel = null) {
static $label = 'Общий';
if (func_num_args() > 0) { $label = $newLabel; }
return $label;
}
echo 'До изменения: ', requestLabel(), "\n";
echo 'После записи: ', requestLabel('Импорт'), "\n";
$cleared = requestLabel(null);
echo 'После очистки: ', $cleared ?? 'подпись отключена', "\n";
echo 'Повторное чтение: ', requestLabel() ?? 'подпись отключена', "\n";
Ожидаемый результат
До изменения: Общий
После записи: Импорт
После очистки: подпись отключена
Повторное чтение: подпись отключена
Вызов без аргумента получает 0 из func_num_args и читает сохранённый label. Вызов с Импорт получает 1 и записывает строку. Явный null — тоже один аргумент: label очищается, return отдаёт null в cleared, и ?? выбирает пояснение отключения. Последний вызов снова читает сохранённое null. Static сохраняет значение между вызовами только внутри текущего выполнения. В новом API отдельные операции чтения и записи обычно проще; здесь показан договор старого интерфейса.
<?php
function showOptions($format = 'csv', $language = 'ru', ...$extra) {
echo 'Формат/язык: ', $format, '/', $language, "\n";
echo 'Позиции func_num_args: ', func_num_args(), "\n";
foreach ($extra as $key => $value) {
echo 'Extra[', $key, ']: ', is_bool($value) ? ($value ? 'true' : 'false') : $value, "\n";
}
}
showOptions(language: 'en');
showOptions(debug: true);
showOptions('csv', 'ru', 'Дополнительная подпись');
Ожидаемый результат
Формат/язык: csv/en
Позиции func_num_args: 2
Формат/язык: csv/ru
Позиции func_num_args: 0
Extra[debug]: true
Формат/язык: csv/ru
Позиции func_num_args: 3
Extra[0]: Дополнительная подпись
Language заполняет вторую объявленную позицию, поэтому список нормализуется до двух значений csv/en. Debug не объявлен и попадает только в extra: func_num_args даёт 0, хотя тело использует defaults. Третий позиционный аргумент входит и в общее число 3, и в extra[0]. Эти числа нельзя складывать для подсчёта уникальных входов. Is_bool в выводе отделяет флаг от текста; скобки у вложенного выбора показывают две разные ветки форматирования.
У функции нет параметров. Результат — int, ноль является обычным значением. В современном PHP вызов из глобальной области даёт Error; старые warning/-1 в описании документации не следует использовать для обработки PHP 8.5.11. Она не проверяет сигнатуру другой функции: для объявления смотрят Reflection, для числа аргументов процесса — $argc, для массива данных — count.
Не используйте это число для проверки обязательных полей формы: HTTP-поля — отдельный массив, а не параметры вызова. При variadic-интерфейсе count($items) считает именно собранный параметр и может лучше выражать задачу. Из-за нормализации named-вызовов нельзя определять по func_num_args, был ли каждый ранний параметр явно записан вызывающим кодом. Чёткий DTO/массив полей или отдельные операции устраняют такую неоднозначность.
Сравните: func_get_arg, func_get_args. Основы: вызов и возврат; передача по ссылке.
function_existsПроверить существование имени функции без её выполнения.
PHP 4+; disabled functions убраны из таблицы PHP 8.0+ · Core
function_exists(string $function): bool| Параметр / результат | Что именно означает |
|---|---|
| function: string | Имя встроенной либо уже объявленной пользовательской функции. Для namespace передайте полное имя. |
| bool | true — имя есть в текущей таблице; false — не найдено. Проверка не отправляет запрос и не выполняет тело. |
| Не проверяет | Метод объекта, конструкцию языка, права пользователя, здоровье интеграции и параметры будущего вызова. |
| Окружение | С PHP 8 отключённые внутренние функции отсутствуют в таблице. Условные объявления появляются после их выполнения; обычной автозагрузки функций здесь нет. |
Проверяет, существует ли уже функция с переданным именем. Саму функцию не вызывает. Это полезно при проверке возможностей окружения или необязательного helper-модуля. true ещё не означает, что конкретные входные данные корректны, внешняя служба доступна или пользователь вправе вызвать действие.
<?php
function localOrderLabel($number) { return 'Заказ №' . $number; }
$name = 'LOCALORDERLABEL';
if (function_exists($name)) {
$text = $name(41);
echo $text, "\n";
}
foreach (['echo', 'missing_manual_helper'] as $candidate) {
if (!function_exists($candidate)) {
echo $candidate, ': не найдено имя функции', "\n";
}
}
Ожидаемый результат
Заказ №41
echo: не найдено имя функции
missing_manual_helper: не найдено имя функции
Function_exists получает строку LOCALORDERLABEL и возвращает true: регистр обычного имени функции не препятствует нахождению. Проверка сама ничего не вызывает. Следующий отдельный $name(41) передаёт 41 в number, return даёт строку в text, затем echo печатает её. Echo — конструкция языка; missing_manual_helper не объявлена, поэтому обе проверки дают false и выбирают диагностические подписи. Произвольное внешнее имя нельзя считать разрешённой командой только потому, что функция существует.
<?php
$required = ['strtoupper', 'str_pad'];
$missing = [];
foreach ($required as $name) {
if (!function_exists($name)) { $missing[] = $name; }
}
if ($missing !== []) {
echo 'Недоступны: ', implode(', ', $missing), "\n";
} else {
$sku = strtoupper('tea');
$label = str_pad($sku, 8, '.', STR_PAD_RIGHT);
echo '|', $label, '|', "\n";
}
Ожидаемый результат
|TEA.....|
Две проверки возвращают true в обычной сборке PHP, список missing остаётся пустым. Затем strtoupper возвращает ASCII-строку TEA, str_pad дополняет её справа до восьми байтов пятью точками, и echo показывает итог с границами. Проверка существования разрешила этот путь, но не проверяет правильность всех будущих входов. Если администратор отключил одну из функций, выполняется сообщение о недоступности. Пример намеренно использует ASCII, а не обещает такую же ширину или преобразование регистра для любого языка.
<?php
namespace ManualShop;
$name = __NAMESPACE__ . '\\optional_label';
if (!function_exists($name)) {
echo "Запасное правило ещё не объявлено: создаём его\n";
function optional_label($number) { return 'Магазин #' . $number; }
}
$text = optional_label(41);
echo $text, "\n";
Ожидаемый результат
Запасное правило ещё не объявлено: создаём его
Магазин #41
Строка name содержит полное ManualShop\optional_label. В начале условное объявление ещё не выполнено; function_exists возвращает false, ! разрешает ветку и функция появляется. Вызов после if передаёт 41, получает Магазин #41 в text и печатает результат. Обычное безусловное объявление верхнего уровня доступно раньше — не переносите этот порядок на него. Строковые имена не получают namespace или use function автоматически. Наличие заранее объявленного совместимого правила позволило бы пропустить fallback.
Функции и методы проверяются разными инструментами. Для наличия метода есть method_exists, для возможности вызова значения из текущей области — is_callable; ни одна из этих проверок не даёт разрешение внешнему пользователю. Для свойств — другой договор, не function_exists. Строковые имена функций не запускают PSR-4 автозагрузку: обычные helper-функции нужно заранее подключить, например через Composer autoload.files.
С PHP 8 внутренние функции из disable_functions удаляются из таблицы, поэтому проверка возвращает false. Зависимости при этом не становятся установленными или исправными автоматически. Есть также функции, существующие независимо от возможности конкретной операции в данной конфигурации. Параметр function — строка имени; произвольный массив/объект не является таким именем и может дать TypeError. Пустое либо неизвестное имя обычно даёт false. Проверяйте конкретную функцию вместо получения всей таблицы для одного ответа.
Сравните: get_defined_functions, call_user_func. Основы: вызов и возврат; передача по ссылке.
get_defined_functionsПолучить два списка уже объявленных функций для ограниченной диагностики.
PHP 4.0.4+; exclude_disabled deprecated PHP 8.5+ · Core
get_defined_functions(bool $exclude_disabled = true): array| Параметр / результат | Что именно означает |
|---|---|
| exclude_disabled: bool | Исторический фильтр. С PHP 8.0 не влияет; с 8.5 явная передача deprecated. В новом коде аргумент опускают. |
| array[internal] | Список имён встроенных функций, доступных текущему runtime. |
| array[user] | Список уже объявленных пользовательских функций; не методов и не файлов автозагрузки. |
| Результат / границы | Снимок текущей таблицы, не список безопасно разрешённых операций. Не фиксируйте порядок/общее число и не публикуйте весь состав приложения. |
Возвращает снимок таблицы уже объявленных функций. Он разделён на internal — встроенные, и user — пользовательские. Это инструмент локальной диагностики расширений и подключения helper-файлов. Для одного известного имени function_exists короче и не создаёт весь список. Для реестра бизнес-команд безопаснее явный разрешённый массив.
Внутри результата два списка строк. Чтобы увидеть их содержимое и сохранить переносимый вывод, ниже выберем одно известное встроенное имя и наше пользовательское имя. Состав остальных функций зависит от расширений и файлов; их полный список для понимания формы не нужен.
<?php
function manual_order_label($number) { return '#' . $number; }
$functions = get_defined_functions();
foreach ($functions['internal'] as $name) {
if ($name === 'strlen') { echo 'Встроенная: ', $name, "\n"; }
}
foreach ($functions['user'] as $name) {
if ($name === 'manual_order_label') { echo 'Пользовательская: ', $name, "\n"; }
}
Ожидаемый результат
Встроенная: strlen
Пользовательская: manual_order_label
Get_defined_functions без аргументов возвращает массив в functions. Ключ internal ведёт к списку встроенных имён, user — к списку пользовательских. Foreach читает реальные строки и выбирает нужные для ограниченного отчёта. Ни strlen, ни manual_order_label не выполняются: это содержимое таблицы, а не callback-вызовы. Для одной проверки существования function_exists проще; ниже применён полный снимок для отчёта о нескольких требуемых helper-именах.
<?php
function manual_export_csv() { return 'CSV'; }
function manual_export_json() { return 'JSON'; }
function unrelated_helper() { return 'Другой модуль'; }
$known = ['manual_export_csv', 'manual_export_json', 'manual_export_xml'];
$defined = get_defined_functions()['user'];
foreach ($known as $name) {
echo $name, ': ', in_array($name, $defined, true) ? 'есть' : 'нет', "\n";
}
Ожидаемый результат
manual_export_csv: есть
manual_export_json: есть
manual_export_xml: нет
Фиксированный known — требования модуля. Отчёт помогает найти забытый include, но не вызывает функции и не доказывает правильность экспорта. В маленькой проверке те же строки проще получить через function_exists; таблица удобна, когда её уже анализируют целиком для ограниченного диагностического отчёта.
Не полагайтесь на длину и порядок списков между машинами. Возвращаемые имена нормализованы в таблице, в частности обычные имена функций представлены в нижнем регистре; namespaced-функции содержат namespace. Это не список методов классов, не содержимое каталогов проекта и не перечень объектов Closure. Условно объявленная функция появится после выполнения своего объявления; ещё не подключённый файл не исследуется автоматически.
Сигнатура с устаревшим bool ниже нужна для чтения старого кода. Неподходящий тип аргумента может вызвать TypeError согласно режиму типов; обычный успешный вызов возвращает массив. Не отдавайте весь диагностический список публичной странице: он раскрывает состав приложения и расширений. Не выбирайте автоматически «все функции с похожим префиксом» для выполнения на основании пользовательского запроса. Явная регистрация callback обеспечивает контролируемый договор; имя в таблице само этого не делает.
Большой список требует создания массива имён при каждом вызове. Не делайте это в цикле для проверки каждой записи заказа. Для одноразового локального исследования снимок разумен; для вызова обработчика его registry уже должен быть готов. Сравните также get_defined_vars, get_defined_constants и get_declared_classes: они исследуют другие сущности, а не заменяют эту функцию.
Сравните: function_exists, call_user_func. Основы: вызов и возврат; передача по ссылке.
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


