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

Дерево руководства
Глава 66 · От первого переключения к протоколу задач
Fiber: пауза, продолжение, состояния и все методы
Как данные выходят через suspend и возвращаются через resume; зачем нужен управляющий код, где находится итог и какие ошибки нельзя скрывать.
Пауза посреди задачи: зачем нужен Fiber
Ответ возвращается ровно в место suspend
Задача просит цену A-10. Управляющий код получает запрос и передаёт ответ 900 обратно.
- 1start(«A-10»)↓
Артикул попадает в параметр callback $sku.
- 2suspend(запрос)↓
Задача сохраняет стек; start возвращает запрос наружу.
- 3resume(900)↓
Управляющий код передаёт ответ в ожидающую задачу.
- 4$price = 900↓
Выражение suspend возвращает ответ; тело продолжает работу.
- 5return → getReturn()
Окончательная подпись читается отдельно от значений пауз.
Fiber не создаёт поток и сам не выполняет сетевой запрос. В примере ответ заранее подготовлен; порядок продолжения задаёт управляющий код.
Для расчёта доставки требуется расстояние до покупателя. Покажем исполнителя, который сообщает о начале, отдаёт вопрос наружу и после ответа 15 км возвращает 300 руб. Управляющий код успевает выполнить свой шаг между вопросом и продолжением.
Начните с обычной функции и анонимной функции. new Fiber(function () { ... }) сохраняет действие в объекте $calculation. ->start() запускает его. Внутренний Fiber::suspend(...) отдаёт вопрос наружу и приостанавливает стек. ->resume(15) продолжает выполнение: выражение suspend внутри получает 15 в $kilometres. После return результат читается отдельным ->getReturn().
- new Fiber сохраняет callback в $calculation
- start запускает расчёт; suspend отдаёт вопрос о расстоянии
- Главный код получает вопрос и готовит товары
- resume(15) → внутри $kilometres = 15
- return формирует 300 руб.; getReturn получает готовую подпись
<?php
declare(strict_types=1);
$calculation = new Fiber(function () {
echo "Расчёт доставки начат\n";
$kilometres = Fiber::suspend('Нужно расстояние до покупателя');
return 'Доставка: ' . ($kilometres * 20) . ' руб.';
});
echo $calculation->start(), "\n";
echo "Главный код может подготовить товары\n";
$calculation->resume(15);
echo $calculation->getReturn(), "\n";
Ожидаемый результат
Расчёт доставки начат
Нужно расстояние до покупателя
Главный код может подготовить товары
Доставка: 300 руб.
new Fiber хранит callback, start начинает его. suspend отдаёт запрос наружу и сохраняет место выполнения. resume(15) передаёт 15 внутрь как результат suspend. getReturn читает итог после завершения. Цена 20 руб./км — учебное правило; сетевого ожидания и параллельного потока здесь нет.
Три разных значения: старт, обмен во время паузы и итог
Пусть задача получает артикул tea, просит цену, а затем возвращает подпись. Аргументы start попадают в параметры callback. Значение suspend уходит наружу как результат start/resume/throw. Вход resume попадает внутрь как результат suspend. Окончательный return callback читают getReturn; start и resume вместо него возвращают null.
start("tea")→ параметр$skuFiber::suspend(["price_for" => $sku])→ start вернул запросresume(25900)→ выражение suspend внутри вернуло 25900 →$priceKopecksreturn $sku . ": " . $priceKopecks→ callback завершён; resume вернул nullgetReturn()→ строка «tea: 25900 коп.»
<?php
declare(strict_types=1);
$task = new Fiber(function (string $sku) {
$priceKopecks = Fiber::suspend(['price_for' => $sku]);
return $sku . ': ' . $priceKopecks . ' коп.';
});
$request = $task->start('tea');
echo 'Нужна цена: ', $request['price_for'], "\n";
$nextRequest = $task->resume(25900);
echo $nextRequest === null && $task->isTerminated() ? "Новых запросов нет: расчёт завершён\n" : "Задача ещё ждёт\n";
echo 'Итог: ', $task->getReturn(), "\n";
Ожидаемый результат
Нужна цена: tea
Новых запросов нет: расчёт завершён
Итог: tea: 25900 коп.
tea идёт в параметр callback; массив price_for выходит через suspend; 25900 идёт обратно через resume; окончательная строка получается через getReturn. resume сам не возвращает return callback. Проверка isTerminated отличает завершение от паузы с null.
null как результат start/resume не доказывает окончание: задача могла вызвать suspend(null). Используйте isSuspended/isTerminated и известный договор задачи. У тела без явного return нормальный итог тоже null.
Карта состояний и допустимые переходы
| Состояние | Что можно сделать | Флаги started / suspended / running / terminated |
|---|---|---|
| Создан, не запущен | start(args) | false / false / false / false |
| Выполняется | Внутри может вызвать suspend; повторный start/resume недопустим | true / false / true / false |
| Приостановлен | resume(value) или throw(error) | true / true / false / false |
| Завершён return | getReturn; повторный запуск невозможен | true / false / false / true |
| Завершён ошибкой | Обработать ошибку снаружи; успешного getReturn нет | true / false / false / true |
Флаг isRunning означает, что начатый вызов start/resume/throw ещё не вернулся. Он не равен «именно этот Fiber сейчас находится на вершине вложенных вызовов»: родитель, вызвавший дочерний Fiber, может считаться running, пока выполняется ребёнок. Текущий объект точно показывает getCurrent. Флаги — сведения о жизненном цикле, а не проверка успешности бизнес-операции.
<?php
declare(strict_types=1);
$task = new Fiber(function () {
Fiber::suspend();
return 'Накладная готова';
});
$task->start();
echo $task->isSuspended() ? "Накладная ждёт продолжения\n" : "Нет паузы\n";
$task->resume();
echo $task->isTerminated() ? "Задача завершена\n" : "Задача ещё выполняется\n";
echo $task->getReturn(), "\n";
Ожидаемый результат
Накладная ждёт продолжения
Задача завершена
Накладная готова
suspend без аргумента отдаёт null. Завершающий resume тоже возвращает null, поэтому значение само по себе не является состоянием Fiber. isSuspended и isTerminated отвечают на разные вопросы. getReturn допустим только после нормального завершения.
Вложенная функция остаётся обычной функцией
Ключевой выигрыш Fiber — пауза может находиться глубоко в стеке вызовов. Функция loadStock возвращает int, а вызывающая её renderStock возвращает string; им не нужно возвращать Generator и писать yield from на каждом уровне. Когда loadStock приостанавливается, сохраняется вся эта цепочка. Вне Fiber такой вызов suspend выдаст FiberError.
- start → renderStock("A-10")
- renderStock вызывает loadStock("A-10")
- loadStock вызывает suspend → наружу уходит артикул; оба обычных вызова ждут
- resume(5) → loadStock возвращает 5
- renderStock возвращает «A-10: 5» → getReturn читает подпись
<?php
function loadStock(string $sku): int {
$stock = Fiber::suspend($sku);
if (!is_int($stock) || $stock < 0) { throw new UnexpectedValueException('Неверный остаток'); }
return $stock;
}
function renderStock(string $sku): string { return $sku . ': ' . loadStock($sku); }
$task = new Fiber(fn() => renderStock('A-10'));
echo 'Запрошен ', $task->start(), "\n";
$task->resume(5);
echo $task->getReturn(), "\n";
Ожидаемый результат
Запрошен A-10
A-10: 5
fn() => — короткая анонимная функция; callback запускается без аргументов. Проверка защищает договор loadStock: внешний ответ не обязан быть правильным. Пример не подключается к складу; он показывает точное место, куда попадёт ответ адаптера.
Приостановка допустима и в callback, который вызван внутренней функцией PHP, например array_map, или в методе Iterator во время foreach. Это не разрешает блокирующий ввод-вывод «само собой»: библиотека должна знать, как получить готовый ответ и возобновить именно эту задачу.
<?php
$task = new Fiber(function () {
return array_map(function ($sku) { return Fiber::suspend($sku); }, ['A-10', 'B-20']);
});
echo $task->start(), "\n";
echo $task->resume(5), "\n";
$task->resume(8);
echo implode(', ', $task->getReturn()), "\n";
Ожидаемый результат
A-10
B-20
5, 8
array_map получает результат callback после каждого возобновления и складывает его в итоговый массив. Снаружи два ответа подставлены последовательно. Этот пример не делает два сетевых запроса параллельными.
Две задачи и маленький управляющий цикл
Планировщик решает, какую приостановленную задачу продолжить. Ниже учебный диспетчер с готовыми ответами: сначала запускает обе задачи, затем выдаёт каждой ответ. Очерёдность видна в выводе; ожидания сети и таймеров здесь нет. Так проще понять обязанности event loop до подключения настоящей библиотеки.
- start задачи «Склад» → ждёт
- start задачи «Доставка» → ждёт
- resume «Склад» с 5 → результат «Склад: 5»
- resume «Доставка» с 300 → результат «Доставка: 300»
<?php
function createTask(string $name): Fiber {
return new Fiber(function () use ($name) {
$reply = Fiber::suspend($name);
return $name . ': ' . $reply;
});
}
$tasks = ['Склад' => createTask('Склад'), 'Доставка' => createTask('Доставка')];
$replies = ['Склад' => 5, 'Доставка' => 300];
foreach ($tasks as $task) { echo 'Ожидает ', $task->start(), "\n"; }
foreach ($tasks as $name => $task) {
if ($task->isSuspended()) { $task->resume($replies[$name]); }
echo $task->getReturn(), "\n";
}
Ожидаемый результат
Ожидает Склад
Ожидает Доставка
Склад: 5
Доставка: 300
use ($name) сохраняет имя в анонимной функции. Реальный диспетчер дополнительно хранит тайм-ауты, ошибки, отмену, ограничения числа задач и соответствие ответа запросу. Этот короткий цикл не заменяет промышленный event loop.
У каждой задачи свой стек и сохранённые локальные значения. Тысячи приостановленных задач могут удерживать много объектов, буферов и открытых соединений. Ограничивайте количество одновременных операций. Если нужен только последовательный поток записей, Generator проще и не требует протокола возобновления целого стека.
Отмена, finally и версия PHP
Отмену можно выразить исключением, отправленным через throw в приостановленную задачу. Это кооперативный протокол: задача должна отдавать управление, а обработчик должен договориться, что означает эта ошибка. Нельзя таким способом прервать произвольный бесконечный цикл, который сейчас работает. finally удобно освобождает ресурс при выходе, но не должен быть единственной гарантией критического бизнес-действия.
- start → задача ждёт
- throw(RuntimeException) → исключение на suspend
- finally освобождает ресурс
- Ошибку получает вызывающий код; getReturn не даёт успех
<?php
declare(strict_types=1);
$task = new Fiber(function () {
try { Fiber::suspend('Ожидаем расстояние'); }
finally { echo "Ресурс расчёта освобождён\n"; }
});
$task->start();
try { $task->throw(new RuntimeException('Покупатель отменил расчёт')); }
catch (RuntimeException $error) { echo $error->getMessage(), "\n"; }
echo $task->isTerminated() ? "Продолжать задачу больше нельзя\n" : "Задача ещё жива\n";
Ожидаемый результат
Ресурс расчёта освобождён
Покупатель отменил расчёт
Продолжать задачу больше нельзя
throw вводит исключение в приостановленное место. Оно раскручивает стек, выполняется finally, затем исключение выходит в вызывающий код. Такая отмена кооперативная: она не прервёт произвольный бесконечный участок, который не отдал управление.
Fiber доступен с PHP 8.1 без отдельной установки. Класс final; наследовать, клонировать или сериализовать остановленную задачу нельзя. До PHP 8.4 переключение Fiber во время деструктора запрещалось. Начиная с 8.4 правила изменились; при сборке циклов деструктор может выполняться в отдельном служебном Fiber. Подробный пример деструктора и сборщика показывает эту границу. Не привязывайте запрос, пользователя или транзакцию к предположению, что любой деструктор выполняется в исходном рабочем контексте.
Следующий шаг: пройдите контракты start/resume/suspend/getReturn и научитесь различать данные промежуточной паузы и окончательный результат. Затем применяйте готовую асинхронную библиотеку по её договору. Для фоновой обработки вне срока HTTP-запроса изучите архитектуру задач и повторы и ограничения запросов.
Чем Fiber помогает и что остаётся планировщику
Fiber сохраняет обычный стек вызовов; это позволяет loadStock вернуть int после паузы без yield from на каждом уровне. Но кто получит данные и когда вызовет resume, решает внешний код. Event loop — цикл, который ждёт готовности операций и продолжает нужные задачи. Сам new Fiber не создаёт ни сеть, ни отдельный процесс.
| Нужен результат | Первый выбор |
|---|---|
| Обойти каталог по одному товару | Generator |
| Приостановить вложенные обычные вызовы | Fiber плюс управляющий код |
| Выполнить блокирующую тяжёлую работу параллельно | Отдельный процесс/подходящая инфраструктура |
| Много HTTP-ожиданий | Неблокирующий транспорт и event loop; Fiber может упростить запись |
Попробуйте. В примере расчёта передайте 0 км, затем отдельно 15 км.
Разобрать решение
Создайте новый Fiber на каждый независимый расчёт: завершённый экземпляр нельзя запустить заново. Получите 0 и 300 руб. Если ноль невозможен по договору, добавьте проверку внутрь callback до вычисления.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
Fiber::__constructСоздать приостанавливаемую задачу из callable, не выполняя её сразу.
PHP 8.1+ · Core
new Fiber(callable $callback)Создать приостанавливаемую задачу из callable, не выполняя её сразу.
| Параметр / результат | Смысл |
|---|---|
| $callback | Функция, Closure или другой допустимый callable. Аргументы придут позже из start. |
| Результат выражения new | Новый объект Fiber; сам конструктор не возвращает результат callback. |
| Ошибки | Недопустимый callable даёт TypeError; ошибки тела возникают уже при start/resume. |
| Версия | PHP 8.1+. final-класс; отдельное расширение не требуется. |
- function объявляет действие
- new Fiber(action) сохраняет его
- isStarted() → false
- start() выполняет тело
<?php
declare(strict_types=1);
$task = new Fiber(function () { echo "Рассчитываем доставку\n"; });
echo "Задача добавлена в список\n";
echo $task->isStarted() ? "Уже выполнялась\n" : "Расчёт ещё не начался\n";
$task->start();
Ожидаемый результат
Задача добавлена в список
Расчёт ещё не начался
Рассчитываем доставку
Конструктор Fiber получает callable, но не запускает его. Это позволяет собрать список задач до первого выполнения. Тело начинает start, как видно по последней строке.
<?php
function makeLabel(string $sku): string { return 'Товар ' . $sku; }
$task = new Fiber('makeLabel');
$task->start('A-10');
echo $task->getReturn(), "\n";
Ожидаемый результат
Товар A-10
Строка makeLabel указывает на заранее объявленную функцию; не принимайте произвольное имя обработчика от посетителя. Эта простая функция не приостанавливается: для неё обычный makeLabel("A-10") практичнее. Пример показывает допустимый callable, а не пользу оборачивания любого вызова в Fiber.
Рядом: Fiber::start, Fiber::isStarted, Fiber::getReturn. Карта состояний.
Fiber::startПервый и единственный запуск Fiber с аргументами его callback.
PHP 8.1+ · Core
Fiber::start(mixed ...$args): mixedПервый и единственный запуск Fiber с аргументами его callback.
| Параметр / результат | Смысл |
|---|---|
| $args | Ноль или больше аргументов callback в том же порядке. Его типы/обязательные параметры продолжают действовать. |
| Результат | Значение первого suspend, либо null при нормальном завершении callback. |
| Ошибки | Повторный start даёт FiberError. Ошибка тела до паузы выходит из start; неверные аргументы могут дать TypeError/ArgumentCountError. |
| Не является | Автоматическим запуском в фоне и способом прочитать окончательный return. |
- start(41) → $orderId = 41
- suspend("Проверить 41")
- start возвращает строку запроса
- После resume окончательный return читается отдельно
<?php
$task = new Fiber(function ($orderId) { Fiber::suspend('Проверить ' . $orderId); });
echo $task->start(41), "\n";
$task->resume();
Ожидаемый результат
Проверить 41
start передаёт вход callback. Сам номер заказа не обязан храниться в глобальной переменной: он принадлежит конкретному запуску задачи.
<?php
$task = new Fiber(function () { return 'готово'; });
var_dump($task->start());
echo $task->getReturn(), "\n";
try { $task->start(); }
catch (FiberError $error) { echo "Нужен новый Fiber\n"; }
Ожидаемый результат
NULL
готово
Нужен новый Fiber
Для новой независимой обработки создайте новый объект. Даже если первый callback сразу завершился без suspend, этот Fiber уже использован.
Рядом: Fiber::__construct, Fiber::resume, Fiber::getReturn. Карта состояний.
Fiber::resumeПродолжить только приостановленный Fiber и передать ему ответ.
PHP 8.1+ · Core
Fiber::resume(mixed $value = null): mixedПродолжить только приостановленный Fiber и передать ему ответ.
| Параметр / результат | Смысл |
|---|---|
| $value | Значение для текущего выражения Fiber::suspend внутри задачи; по умолчанию null. |
| Результат | Значение следующего suspend либо null при нормальном завершении. Итог callback — в getReturn. |
| Ошибка состояния | FiberError для нового, выполняющегося или уже завершённого Fiber. |
| Ошибка задачи | Неперехваченное исключение из продолженного тела выходит из resume. |
- suspend("цена") → ждёт
- resume(900) → $price = 900
- Тело делает return $price * 2
- resume возвращает null; getReturn возвращает 1800
<?php
declare(strict_types=1);
$task = new Fiber(function () {
$priceKopecks = Fiber::suspend('Нужна цена упаковки чая');
return $priceKopecks * 2;
});
echo $task->start(), "\n";
$task->resume(25900);
echo 'Две упаковки: ', $task->getReturn(), " коп.\n";
Ожидаемый результат
Нужна цена упаковки чая
Две упаковки: 51800 коп.
resume передаёт 25900 в приостановленный suspend как его результат. Код после паузы использует полученную цену. Окончательная сумма находится в getReturn; учебный ответ передан локально, сеть не выполнялась.
<?php
$task = new Fiber(function () { Fiber::suspend(); });
try { $task->resume(); }
catch (FiberError $error) { echo "Сначала start\n"; }
$task->start();
if ($task->isSuspended()) { $task->resume(); }
echo $task->isTerminated() ? "Готово\n" : "Ждёт\n";
Ожидаемый результат
Сначала start
Готово
Проверка isSuspended пригодна управляющему коду. Она не заменяет обработку ошибок самого callback.
Рядом: Fiber::suspend, Fiber::start, Fiber::isSuspended. Карта состояний.
Fiber::throwВозобновить ожидание исключением вместо успешного ответа.
PHP 8.1+ · Core
Fiber::throw(Throwable $exception): mixedВозобновить ожидание исключением вместо успешного ответа.
| Параметр / результат | Смысл |
|---|---|
| $exception | Объект Exception или Error. Он будет выброшен из текущего suspend внутри Fiber. |
| Результат | Значение следующего suspend, либо null при нормальном завершении после обработки ошибки. |
| Если не поймано | Исключение выходит в вызывающий throw код; Fiber становится завершённым с ошибкой. |
| Состояние | Требуется приостановленный Fiber. Иначе FiberError, а не доставка вашего исключения в тело. |
- Задача suspend → ожидает цену
- Снаружи throw(RuntimeException)
- Внутри catch выбирает ручной расчёт
- Следующий suspend отдаёт «Проверить вручную»
<?php
$task = new Fiber(function () {
try { Fiber::suspend('Запросить цену'); }
catch (RuntimeException $error) { Fiber::suspend('Проверить вручную'); }
});
echo $task->start(), "\n";
echo $task->throw(new RuntimeException('Тайм-аут')), "\n";
$task->resume();
Ожидаемый результат
Запросить цену
Проверить вручную
catch может создать новый этап ожидания. Поэтому после throw задача не обязательно завершена — это определяется тем, что сделало тело.
<?php
$task = new Fiber(function () { Fiber::suspend(); });
$task->start();
try { $task->throw(new RuntimeException('Доставка недоступна')); }
catch (RuntimeException $error) { echo $error->getMessage(), "\n"; }
var_dump($task->isTerminated());
try { $task->getReturn(); }
catch (FiberError $error) { echo "Успешного результата нет\n"; }
Ожидаемый результат
Доставка недоступна
bool(true)
Успешного результата нет
Отмена и ошибка не являются обычным return. Не пытайтесь считать getReturn успешным только по isTerminated().
Рядом: Fiber::resume, Fiber::getReturn, Fiber::isTerminated. Карта состояний.
Fiber::getReturnПрочитать окончательный return callback после нормального завершения.
PHP 8.1+ · Core
Fiber::getReturn(): mixedПрочитать окончательный return callback после нормального завершения.
| Параметр / результат | Смысл |
|---|---|
| Параметры | Нет. |
| Результат | Значение return callback; при нормальном выходе без значения — null. |
| FiberError | До запуска, во время ожидания/работы либо после окончания исключением. |
| Не делает | Не запускает задачу, не возобновляет её и не ждёт сеть. |
- start → callback считает 900 + 300
- return 1200 → нормальное окончание
- start вернул null
- getReturn → 1200
<?php
$task = new Fiber(function () { return 900 + 300; });
var_dump($task->start());
echo 'Итого: ', $task->getReturn(), "\n";
echo 'Повторное чтение: ', $task->getReturn(), "\n";
Ожидаемый результат
NULL
Итого: 1200
Повторное чтение: 1200
Повторное чтение не запускает расчёт снова. В этом примере Fiber избыточен для сложения; в составной задаче итог получают тем же методом после последнего ответа.
<?php
$task = new Fiber(function () { $stock = Fiber::suspend(); return $stock > 0; });
$task->start();
try { $task->getReturn(); }
catch (FiberError $error) { echo "Ещё ждём остаток\n"; }
$task->resume(5);
echo $task->getReturn() ? "Можно показать кнопку заказа\n" : "Товар закончился\n";
Ожидаемый результат
Ещё ждём остаток
Можно показать кнопку заказа
До resume решение не вычислено, getReturn даёт FiberError. Ответ 5 позволяет вернуть true и включить действие интерфейса. Булево значение полезно как решение, но не объясняет ошибки сети — их надо обрабатывать отдельно.
Рядом: Fiber::isTerminated, Fiber::resume, Fiber::throw. Карта состояний.
Fiber::isStartedУзнать, был ли Fiber уже запущен, в том числе если он теперь завершён.
PHP 8.1+ · Core
Fiber::isStarted(): boolУзнать, был ли Fiber уже запущен, в том числе если он теперь завершён.
| Параметр / результат | Смысл |
|---|---|
| Параметры | Нет. |
| Результат | false до первого запуска, true после начала исполнения. |
| Не означает | Готовность результата, текущую активность или возможность повторного start. |
- new Fiber → false
- start → true
- callback завершён → всё ещё true
<?php
declare(strict_types=1);
$task = new Fiber(function () { return 'Смета доставки: 300 руб.'; });
echo $task->isStarted() ? "Уже запускалась\n" : "Задача новая\n";
$task->start();
echo $task->getReturn(), "\n";
echo $task->isStarted() ? "Повторный start запрещён\n" : "Можно запустить\n";
echo $task->isTerminated() ? "Для нового расчёта создайте новый Fiber\n" : "Расчёт продолжается\n";
Ожидаемый результат
Задача новая
Смета доставки: 300 руб.
Повторный start запрещён
Для нового расчёта создайте новый Fiber
isStarted остаётся true после завершения и не означает «выполняется прямо сейчас». isTerminated сообщает конец. Чтобы пересчитать другой заказ, создают другой Fiber; готовый результат первого можно читать повторно.
<?php
$tasks = [new Fiber(function () { echo "Проверка склада\n"; })];
foreach ([1, 2] as $pass) {
foreach ($tasks as $task) { if (!$task->isStarted()) { $task->start(); } }
}
Ожидаемый результат
Проверка склада
Второй проход управляющего кода не выполняет повторный start. Для перезапуска самой проверки понадобится новый Fiber, а не изменение флага.
Рядом: Fiber::start, Fiber::isSuspended, Fiber::isTerminated. Карта состояний.
Fiber::isSuspendedПроверить, ждёт ли задача в точке suspend и может ли принять ответ.
PHP 8.1+ · Core
Fiber::isSuspended(): boolПроверить, ждёт ли задача в точке suspend и может ли принять ответ.
| Параметр / результат | Смысл |
|---|---|
| Параметры | Нет. |
| Результат | true только для приостановленного Fiber. |
| Практика | Проверка перед resume/throw. Не определяет, какой ответ нужен и не проверяет его данные. |
- Новый → false
- start до suspend → true
- resume до return → false
<?php
declare(strict_types=1);
$task = new Fiber(function () { return Fiber::suspend('Нужно расстояние'); });
echo $task->isSuspended() ? "Ждёт ответ\n" : "Ожидание ещё не началось\n";
echo $task->start(), "\n";
echo $task->isSuspended() ? "Можно передать расстояние\n" : "Ответ сейчас не нужен\n";
$task->resume(15);
echo $task->isSuspended() ? "Ждёт дальше\n" : "Ожидание закончено\n";
echo 'Получено: ', $task->getReturn(), " км\n";
Ожидаемый результат
Ожидание ещё не началось
Нужно расстояние
Можно передать расстояние
Ожидание закончено
Получено: 15 км
Созданный Fiber ещё не приостановлен. После suspend он принимает resume/throw, после завершения — уже нет. Поздний ответ нужно сверять с состоянием, а не отправлять в любой сохранённый объект задачи.
<?php
$task = new Fiber(function () { return Fiber::suspend(); });
$task->start();
foreach ([5, 6] as $reply) {
if ($task->isSuspended()) { $task->resume($reply); }
else { echo "Повторный ответ пропущен\n"; }
}
echo 'Результат: ', $task->getReturn(), "\n";
Ожидаемый результат
Повторный ответ пропущен
Результат: 5
Это только демонстрация защиты жизненного цикла. Если задача имеет несколько ожиданий, одного isSuspended мало: нужен идентификатор этапа/запроса, иначе второй ответ может попасть в следующее ожидание.
Рядом: Fiber::resume, Fiber::throw, Fiber::getReturn. Карта состояний.
Fiber::isRunningУзнать, выполняется ли вызов входа в Fiber, который ещё не вернулся.
PHP 8.1+ · Core
Fiber::isRunning(): boolУзнать, выполняется ли вызов входа в Fiber, который ещё не вернулся.
| Параметр / результат | Смысл |
|---|---|
| Параметры | Нет. |
| Результат | true между входом start/resume/throw и возвратом этого вызова. |
| Вложенные Fiber | Родитель может оставаться running, пока дочерний Fiber находится на вершине. Для текущего контекста используйте getCurrent. |
| Не является | Измерением загрузки CPU или доказательством параллельности. |
- В callback getCurrent()->isRunning() → true
- suspend отдаёт управление
- Снаружи isRunning() → false
<?php
declare(strict_types=1);
$task = new Fiber(function () {
echo Fiber::getCurrent()->isRunning() ? "Сейчас готовим запрос доставки\n" : "Задача не выполняется\n";
Fiber::suspend();
});
$task->start();
echo $task->isRunning() ? "Расчёт продолжает исполняться\n" : "Расчёт приостановлен, управление снаружи\n";
$task->resume();
Ожидаемый результат
Сейчас готовим запрос доставки
Расчёт приостановлен, управление снаружи
Внутри callback текущая задача running. После suspend выполнение вернулось вызывающему коду и задача suspended. Для вложенных Fiber флаг running родителя имеет отдельную тонкость: он не всегда означает, что именно родитель — текущий Fiber, это следующий опыт.
<?php
$parent = new Fiber(function () {
$parent = Fiber::getCurrent();
$child = new Fiber(function () use ($parent) {
echo $parent->isRunning() ? "Родительский стек ещё активен\n" : "Родитель не активен\n";
echo Fiber::getCurrent() === $parent ? "Текущий — родитель\n" : "Текущий — дочерний Fiber\n";
});
$child->start();
});
$parent->start();
Ожидаемый результат
Родительский стек ещё активен
Текущий — дочерний Fiber
Родитель вызвал start дочернего Fiber, а не собственный suspend. Поэтому наблюдаемый running и идентичность getCurrent отвечают на разные вопросы. Не используйте один флаг как универсальную проверку контекста.
Рядом: Fiber::getCurrent, Fiber::isSuspended, Fiber::start. Карта состояний.
Fiber::isTerminatedУзнать, закончилось ли выполнение, успешно или с исключением.
PHP 8.1+ · Core
Fiber::isTerminated(): boolУзнать, закончилось ли выполнение, успешно или с исключением.
| Параметр / результат | Смысл |
|---|---|
| Параметры | Нет. |
| Результат | true после return, конца тела или неперехваченного исключения; иначе false. |
| Не означает | Успешное выполнение заказа/запроса. Ошибку нужно хранить и обрабатывать отдельно. |
- Нормальный return → terminated true + доступен getReturn
- Непойманное исключение → terminated true + getReturn бросит FiberError
<?php
declare(strict_types=1);
$task = new Fiber(function () { return 'Доставка: 300 руб.'; });
echo $task->isTerminated() ? "Смета готова\n" : "Смета ещё не рассчитана\n";
$task->start();
if ($task->isTerminated()) { echo $task->getReturn(), "\n"; }
Ожидаемый результат
Смета ещё не рассчитана
Доставка: 300 руб.
isTerminated проверяет окончание callback. В этом опыте окончание нормальное, поэтому getReturn доступен. При необработанном исключении Fiber также может быть terminated, но успешного результата не будет — это проверяется следующим примером.
<?php
$task = new Fiber(function () { throw new RuntimeException('Нет соединения'); });
$failed = false;
try { $task->start(); }
catch (RuntimeException $error) { $failed = true; echo $error->getMessage(), "\n"; }
echo $task->isTerminated() ? "Задача закончилась\n" : "Задача ещё жива\n";
echo $failed ? "Показать ошибку\n" : "Показать результат\n";
Ожидаемый результат
Нет соединения
Задача закончилась
Показать ошибку
Отдельный флаг failed хранит смысл завершения. isTerminated сообщает только состояние исполнения. Пользователю надо показать ошибку или результат согласно фактическому исходу, а не считать любой конец успехом.
Рядом: Fiber::getReturn, Fiber::throw, Fiber::isStarted. Карта состояний.
Fiber::getCurrentПолучить объект Fiber, в котором сейчас выполняется код, либо null снаружи.
PHP 8.1+ · Core
Fiber::getCurrent(): ?FiberПолучить объект Fiber, в котором сейчас выполняется код, либо null снаружи.
| Параметр / результат | Смысл |
|---|---|
| Параметры | Нет. |
| Результат | Текущий Fiber; null в основном коде вне него. ?Fiber означает Fiber или null. |
| Применение | Проверить договор адаптера, привязать локальный контекст задачи по идентичности объекта. |
| Осторожно | Получение объекта не даёт права возобновить его из него же: resume требует состояние suspended. |
- Основной код → null
- Внутри callback → объект текущей задачи
- После завершения → основной код снова null
<?php
echo Fiber::getCurrent() === null ? "Обычный внешний код\n" : "Есть текущая задача\n";
$task = new Fiber(function () { echo Fiber::getCurrent() instanceof Fiber ? "Внутри Fiber\n" : "Снаружи\n"; });
$task->start();
echo Fiber::getCurrent() === null ? "Обычный внешний код\n" : "Есть текущая задача\n";
Ожидаемый результат
Обычный внешний код
Внутри Fiber
Обычный внешний код
getCurrent возвращает выполняющийся Fiber либо null. До start и после возврата из него текущей задачи нет. Следующий awaitStock использует это, чтобы отказаться от suspend в неподходящем месте.
<?php
function awaitStock(string $sku): int {
if (Fiber::getCurrent() === null) { throw new LogicException('Нужен контекст задачи'); }
$stock = Fiber::suspend($sku);
if (!is_int($stock)) { throw new UnexpectedValueException('Нужен целый остаток'); }
return $stock;
}
try { awaitStock('A-10'); }
catch (LogicException $error) { echo $error->getMessage(), "\n"; }
$task = new Fiber(fn() => awaitStock('A-10'));
$task->start();
$task->resume(5);
echo $task->getReturn(), "\n";
Ожидаемый результат
Нужен контекст задачи
5
Своя ошибка объясняет требование API до вызова suspend. Если нужен синхронный режим, его следует реализовать отдельным ясным путём, а не возвращать фиктивный остаток 0.
Рядом: Fiber::suspend, Fiber::isRunning, Fiber::__construct. Карта состояний.
Источники и обсуждения
- PHP: Fiber
- PHP: Fibers language overview
- Обсуждение: Fiber не продолжает основной код автоматически без приостановки. — обсуждение проблемы
- Обсуждение: Промежуточное значение start/resume перепутали с итогом callback. — обсуждение проблемы
- Обсуждение: suspend вызван в основном коде после возврата из start. — обсуждение проблемы
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


