Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

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

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 147 · От первого вызова к справочнику

Вызов функций и callback: полный справочник Function Handling

От выбора действия и передачи аргументов до диагностики вызова, наследования static-контекста, shutdown и ticks. Все 13 официальных функций, включая историческую create_function.

Сначала полезное действие: подпись заказа из одной функции

Наглядная модель

От вызова orderLabel(41) к строке на экране

Объявление сохраняет правило построения подписи. Его тело начинает работать при вызове внутри echo.

  1. echo orderLabel(41)

    Чтобы вывести ответ, сначала нужно вычислить вызов.

  2. function orderLabel($number)

    Аргумент 41 связывается с локальным параметром number.

  3. return «Заказ №» . $number

    Тело строит строку «Заказ №41» и завершает вызов.

  4. Обратно в выражение echo

    Место orderLabel(41) занимает возвращённая строка.

  5. Вывод: Заказ №41

    echo печатает результат; дальнейших инструкций в первом примере нет.

Функция не печатает сама. Если сохранить результат в переменную, тот же return вернётся в правую часть присваивания.

Страница заказа и письмо должны показывать одинаковую подпись. Вынесем построение текста в функцию. Сперва вызовем её обычным способом; затем сможем передать выбор действия в другой участок программы. Здесь ничего не отправляется по сети: результат — строка в консоли.

Вызов orderLabel(41) и возвращение к echo
  1. В месте orderLabel(41) PHP выбирает объявление функции
  2. Её параметр $number получает число 41
  3. Точка соединяет «Заказ №» с 41; return возвращает строку
  4. Строка занимает место вызова; 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) при выборе, получится уже строка результата, а не само действие. Для одного заранее известного вызова обычная запись проще.

Когда выполняется callback
  1. Записать $formatter = 'orderLabel': сохранить выбор
  2. call_user_func($formatter, 41): вызвать сейчас
  3. register_shutdown_function($callback): зарегистрировать на завершение
  4. Действие выполняет тот механизм, которому вы его передали; слово 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. Эта глава не подменяет его: работу замыканий, захват состояния и области доступа продолжайте в главе «Замыкания».

Список аргументов — отдельный договор вызова

Для подписи достаточно номера, для строки отчёта понадобятся ещё формат и язык. Нужно понимать, по чему входные значения попадут в параметры: по позиции или по имени. Ассоциативный массив запроса не становится правильным списком вызова автоматически.

Явное сопоставление для makeLabel($number, $prefix)
  1. Ввод проверен: номер 41, префикс «Заказ»
  2. Позиционный список [41, 'Заказ']: первый элемент → number, второй → prefix
  3. Именованный список ['prefix' => 'Заказ', 'number' => 41]: ключ → имя параметра
  4. Тело возвращает одну строку; обёртка передаёт её в место вызова

С PHP 8 строковые ключи в call_user_func_array — именованные аргументы. Переименование параметра библиотеки может сломать такого клиента. Числовые ключи передаются по порядку обхода массива, а не по своему значению. Числовой аргумент после именованного даёт Error; неизвестное имя обычно тоже, кроме предусмотренного сбора в variadic-параметр.

Если задача требует позиционного вызова, сформируйте список в явном порядке. array_values снимает ключи, но не угадывает, какой параметр вы имели в виду: переставленный вход останется переставленным. Ссылочный параметр требует отдельного договора с &; обычный call_user_func не передаёт аргументы по ссылке.

Разберём новые обозначения до опыта: int перед $count задаёт тип входного параметра, : int после скобок — тип результата. declare(strict_types=1) включает строгую скалярную проверку для соответствующих вызовов из этого файла. try выполняет попытку, catch (TypeError $error) получает ошибку типа и позволяет продолжить код после обработанного отказа. Здесь строку специально передают вместо числа, чтобы увидеть обе границы вызова.

Прямой вызов и call_user_func отклоняют неподходящий типСамостоятельный пример
<?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 вызывает ваш код

Одно выполнение, три места работы
  1. Основной код вызывает функции и получает результаты
  2. В участках declare(ticks=N) дополнительные точки выполнения вызывают tick callback
  3. Скрипт заканчивается или вызывает exit: выполняется очередь shutdown callback
  4. Процесс или среда запроса завершает оставшуюся очистку; принудительное убийство не даёт гарантии 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.0create_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.5Tick 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) тоже поддерживается.

Внутри call_user_func('orderLabel', 41)
  1. Выбрать функцию по первому аргументу
  2. Передать 41 в её $number
  3. Выполнить тело и получить строку
  4. Вернуть эту строку вызывающему 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-вариант, чтобы не спутать два уровня.

Ошибка самого вызова и ошибка внутри callback различаютсяСамостоятельный пример
<?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.

Позиционный args = [41, 'Заказ']
  1. Массив содержит два элемента
  2. 41 попадает в $number, «Заказ» — в $prefix
  3. makeLabel возвращает строку
  4. Результат возвращается из 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. Это не отдельный поток и не долговечная очередь заданий.

Порядок основного кода и завершения
  1. register_shutdown_function сохраняет callback
  2. echo основного кода выполняется сейчас
  3. Основной код заканчивается
  4. PHP вызывает сохранённый callback с его аргументами
  5. Его 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Аргументы, передаваемые наблюдателю при каждом вызове.
booltrue при успешной регистрации; документированный тип bool. Неверный callable в PHP 8 — TypeError, не универсальный false.
Связанный синтаксисdeclare(ticks=N), где N — положительный интервал tickable-инструкций; это не миллисекунды и не номер строки.

Позволяет подключить короткое наблюдение к tick-точкам PHP. Например, при локальном исследовании старого алгоритма нужно убедиться, что обработчик вообще получает управление. Для обычного приложения, прогресса заказа или ограничения запросов это обычно лишний механизм: явный вызов после обработанной строки яснее и предсказуемее.

Два независимых условия для тика
  1. register_tick_function сохраняет callback
  2. declare(ticks=1) включает точки вызова в данном участке исходника
  3. После подходящей инструкции PHP вызывает callback
  4. unregister_tick_function снимает регистрацию
  5. Один 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 остаётся обычным значением переменной, но больше не вызывается по этой регистрации.

Путь ближайшего примера
  1. Observer сохраняет текущее имя документа.
  2. Первый tick-участок устанавливает №42, и снимок получает №42.
  3. Unregister снимает именно сохранённый observer.
  4. Второй участок устанавливает №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: stringPHP-тело для динамической компиляции; подстановка данных в него могла стать исполнением чужого кода.
Исторический результатСгенерированное имя функции или false; это не объект Closure.
Современный PHPФункция удалена: обычный вызов вызывает Error. Используйте анонимную функцию, именованную функцию или настроенный callable.

В старом модуле можно встретить функцию, которая собирает PHP-код строкой и получает имя созданной функции. Так раньше имитировали анонимные функции. Сегодня сначала распознайте историческую запись, затем перенесите логику в обычный Closure: данные должны оставаться данными, а тело программы — исходным кодом.

Как узнать старую конструкциюФрагмент или команда
<?php
// Исторический фрагмент для чтения; НЕ запускать в PHP 8.
$format = create_function('$number', 'return "Заказ №" . $number;');

Первой строкой описывались параметры, второй — исполняемое тело. В современном PHP такого имени функции нет: вызов даст Error. Не заменяйте отсутствие функции собственным eval-полифиллом. Этот исторический фрагмент не исполняется.

Замена кода-строки на Closure
  1. Было: строка параметров и строка тела
  2. Стало: function ($number) { return ...; } в исходнике
  3. Переменная $format хранит объект Closure
  4. $format(41) запускает его и возвращает строку
Современная замена без генерации PHP-кодаСамостоятельный пример
<?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
Опт #41

use сохраняет значение для будущего вызова; 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 → тело Document::label → static::PREFIX
  1. Invoice::compare(41) начинает работу в Invoice
  2. Callback указывает на тело Document::label
  3. Обычный call_user_func использует Document как called class
  4. forward_static_call передаёт подходящий текущий called class Invoice
  5. Тот же static::PREFIX читает разную константу
Одно тело, два варианта static-контекстаСамостоятельный пример
<?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
Счёт #41

extends задаёт наследование, 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:300

export объявлен один раз. В каждом вызове выбирается тело 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; отсутствие ключа не заменяется автоматическим значением.

Если callback изменяет переданный списокСамостоятельный пример
<?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. Возврат массива не вызывает переданные значения как функции.

orderSummary(41, 3) → func_get_args()
  1. Вызов передаёт два значения
  2. Текущий frame функции хранит её аргументы
  3. func_get_args() создаёт массив [41, 3]
  4. foreach читает позиции 0 и 1
  5. Это значения вызова, не имена $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 получает индекс и значение массива. Имена переменных вызывающего кода здесь не восстанавливаются; если нужен именованный договор данных, передайте явный массив с разрешёнными полями.

Старая обёртка и явный variadic делают одинаковую строкуСамостоятельный пример
<?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.

Различить используемые defaults, обычный список и дополнительные именаСамостоятельный пример
<?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 передайте полное имя.
booltrue — имя есть в текущей таблице; 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-именах.

Проверить ограниченный набор собственных 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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.