API Reference
Полный справочник по каждому публичному типу в Flytachi\Winter\Thread. Сигнатуры точно соответствуют исходному коду; для каждого элемента перечислены параметры, возвращаемые значения, исключения и неочевидная семантика.
API с высоты птичьего полёта
Поверхность делится на два уровня. Большинству приложений нужен только публичный API — три типа. Низкоуровневый API нужен для построения собственного пула, планировщика или супервизора прямо на сырых примитивах.
Публичный API — то, что вы используете
| Тип | Вид | Для чего используется |
|---|---|---|
Runnable |
интерфейс | описать задачу — поместите свою логику в run() |
Thread |
класс | запустить и управлять одной задачей как фоновым процессом |
Engine |
интерфейс | настроить доставку/исполнение один раз при инициализации |
AdaptiveEngine · ManualEngine |
класс | два движка — самонастраивающийся / явный |
Низкоуровневый и расширяющий API — то, на чём вы строите
| Тип | Вид | Роль |
|---|---|---|
Launcher · CliLauncher |
интерфейс / класс | породить процесс → вернуть дескриптор |
ProcessHandle |
класс | управлять одним процессом (reap/join/detach/сигнал/чтение) |
LaunchSpec |
DTO | все параметры запуска в одном объекте-значении |
PayloadTransport · Pipe / TempFile / Shm |
интерфейс / класс | стратегия доставки payload |
StagedPayload |
DTO | результат подготовки, который потребляет launcher |
Runner · AdaptiveRunner |
интерфейс / класс | исполнение на стороне потомка |
Signal |
класс | POSIX-хелперы по сырому PID (учитывают зомби) |
ThreadException |
класс | единственное исключение библиотеки |
Четыре интерфейса (Engine, Launcher, Runner, PayloadTransport) — это точки расширения, принимающие вашу собственную реализацию; всё остальное — конкретные типы, которые вы потребляете.
Публичный API
Runnable (interface)
Flytachi\Winter\Thread\Runnable — контракт задачи.
public function run(array $args): voidПоместите свою логику в run(); она выполняется в рабочем процессе.
| Параметр | Тип | Описание |
|---|---|---|
$args |
array<string, string|bool> |
значения конкретного запуска из start() — флаги приходят как булево true, все остальные значения как строки |
Возвращает ничего. Сигнализируйте о результате через код возврата (return/нормальное завершение → 0; выброс исключения → ненулевой) или побочные эффекты (файл/БД/очередь).
Контракт: реализующий объект должен быть сериализуемым — никаких живых ресурсов (PDO, сокеты, потоки) в его свойствах; открывайте их внутри run(). Непойманное исключение перехватывается runner’ом, логируется в STDERR со стеком трассировки и превращается в ненулевой код возврата.
Thread (final class)
Flytachi\Winter\Thread\Thread — высокоуровневый фасад над одним процессом. Изменяемый; защищает от конкурентных запусков.
__construct
new Thread(
Runnable $runnable,
string $namespace = '',
?string $name = null,
?string $tag = null,
)| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$runnable |
Runnable |
— | задача для выполнения |
$namespace |
string |
'' |
логическая группировка, отображается в заголовке процесса ОС |
$name |
?string |
null |
имя задачи; при null — короткое имя класса Runnable (или 'anonymous' для анонимных классов) |
$tag |
?string |
null |
различитель экземпляра; показывается как @tag (по умолчанию @runnable в заголовке) |
Конструирование ничего не запускает. Заголовок процесса — WinterThread <namespace> -> <name>@<tag> (только там, где существует cli_set_process_title()).
Статические — привязка движка
| Метод | Возвращает | Описание |
|---|---|---|
Thread::bindEngine(Engine $engine) |
void |
задать движок на весь процесс (вызвать один раз при инициализации) |
Thread::engine() |
Engine |
текущий движок; лениво создаёт AdaptiveEngine по умолчанию, если ни один не привязан |
start
start(
array $arguments = [],
bool $debugMode = false,
?string $outputTarget = '/dev/null',
bool $detached = false,
): intСериализует задачу, запускает процесс и немедленно возвращает управление.
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$arguments |
array<string, scalar|null> |
[] |
аргументы конкретного запуска, доступные в $args внутри run(). true → флаг без значения; false/null → отбрасывается; прочие скаляры → строки; нескаляры игнорируются |
$debugMode |
bool |
false |
включить E_ALL + display_errors в потомке |
$outputTarget |
?string |
'/dev/null' |
'/dev/null' отбрасывает (без pipe); путь дозаписывает (режим a); null направляет в pipe родителю для readOutput()/readError() |
$detached |
bool |
false |
демонизировать (fork+setsid) для fire-and-forget без зомби |
Возвращает int — PID запущенного процесса (в detached-режиме — эфемерный PID launcher’а). Выбрасывает ThreadException, если поток уже жив или процесс не удаётся запустить.
Broken pipe
Передача null направляет stdout/stderr в pipe родителю. Если родитель их никогда не вычитывает, буфер pipe в ОС (~64 КБ) заполняется, потомок блокируется на write(), что в итоге приводит к Broken pipe. Передавайте null, только если активно вызываете readOutput()/readError(); иначе оставляйте значение по умолчанию '/dev/null'. См. Вывод и Broken Pipe.
Жизненный цикл и ожидание
| Метод | Возвращает | Описание |
|---|---|---|
join(int $timeout = 0) |
?int |
блокирует (опрос каждые 50 мс) до завершения; возвращает код возврата, null по таймауту (секунды; 0 = бесконечно) или -1, если не был запущен или воркер убит сигналом. Пожинает по завершении |
reap() |
bool |
неблокирующий: true, если завершён/отсутствует (и пожат, код возврата установлен), false, если ещё выполняется |
detach() |
void |
прекратить отслеживание (неблокирующий; без proc_close). После этого isAlive() → false, reap() → true, сигналы → false, код возврата никогда не собирается. Внимание: detach() ≠ detached-режим |
Состояние
| Метод | Возвращает | Описание |
|---|---|---|
getPid() |
?int |
PID запущенного процесса или null до start(); PID launcher’а в detached-режиме |
isAlive() |
bool |
выполняется ли процесс сейчас? false до запуска / после завершения / после detach() |
getExitCode() |
?int |
код возврата после пожинания (join/reap); -1, если воркер убит сигналом; null до пожинания и null навсегда после detach() |
Сигналы (требуют ext-posix; каждый возвращает false, если не выполняется)
| Метод | Сигнал | Описание |
|---|---|---|
pause() |
SIGSTOP | приостановить (неблокируемый) |
resume() |
SIGCONT | возобновить приостановленный воркер |
interrupt() |
SIGINT | эквивалент Ctrl+C (перехватываемый) |
terminate() |
SIGTERM | запрос корректной остановки (перехватываемый) |
kill() |
SIGKILL | принудительное убийство (неблокируемое) |
Каждый возвращает bool — true, если сигнал был отправлен. Чтобы отреагировать корректно, задача должна установить обработчик; см. Корректное завершение.
Вывод (непустой только при запуске с outputTarget: null)
| Метод | Возвращает | Описание |
|---|---|---|
readOutput() |
string |
доступный прямо сейчас STDOUT (неблокирующий); '', если ничего / не в pipe |
readError() |
string |
доступный прямо сейчас STDERR (неблокирующий); '', если ничего / не в pipe |
Метаданные
| Метод | Возвращает | Описание |
|---|---|---|
getNamespace() |
string |
пространство имён |
getName() |
string |
разрешённое имя задачи |
getTag() |
?string |
тег или null |
Engine (interface)
Flytachi\Winter\Thread\Engine\Engine — корень конфигурации/стратегии, используется только на стороне родителя. У него нет метода для стороны потомка: воркер запускает отдельный AdaptiveRunner. security() родителя передаётся потомку через переменную окружения WINTER_THREAD_SECRET.
| Метод | Возвращает | Описание |
|---|---|---|
transport() |
PayloadTransport |
как доставляется payload |
launcher() |
Launcher |
как порождается процесс |
binaryPath() |
string |
абсолютный путь к бинарнику PHP CLI |
runnerPath() |
string |
абсолютный путь к скрипту wRunner |
security() |
?DefaultSecurityProvider |
Opis\Closure\Security\DefaultSecurityProvider для подписи или null, если секрета нет |
AdaptiveEngine (final readonly class, default)
Flytachi\Winter\Thread\Engine\AdaptiveEngine — самонастраивающийся; неизменяемый.
new AdaptiveEngine(
?string $secret = null,
?PayloadTransport $transport = null,
?string $binaryPath = null,
?string $runnerPath = null,
?Launcher $launcher = null,
)| Параметр | Тип | Разрешаемое значение по умолчанию при null |
|---|---|---|
$secret |
?string |
переменная окружения WINTER_THREAD_SECRET, иначе null (без подписи) |
$transport |
?PayloadTransport |
TempFileTransport при активном рантайме Swoole (выполняющаяся корутина или включённые хуки рантайма), иначе PipeTransport |
$binaryPath |
?string |
CLI SAPI → PHP_BINARY; не-CLI (FPM/CGI) → PHP_BINDIR/php, если исполняемый, иначе 'php' |
$runnerPath |
?string |
упакованный wRunner |
$launcher |
?Launcher |
CliLauncher, собранный из разрешённых binary/runner/transport + окружение потомка (несущее секрет); переданный launcher используется как есть |
Неизменяемый — чтобы изменить один аспект, создайте новый экземпляр.
ManualEngine (final class)
Flytachi\Winter\Thread\Engine\ManualEngine — явный, с чистого листа. Неизменяемые wither-методы (каждый возвращает клон); незаданная обязательная часть выбрасывает ThreadException при обращении ("ManualEngine: <part> is not configured.").
| Метод | Возвращает | Описание |
|---|---|---|
withTransport(PayloadTransport $t) |
static |
задать транспорт |
withBinaryPath(string $path) |
static |
задать путь к бинарнику PHP |
withRunnerPath(string $path) |
static |
задать путь к wRunner |
withSecurity(string $secret) |
static |
включить подпись этим секретом |
withLauncher(Launcher $l) |
static |
использовать свой launcher (в обход умолчания) |
При своём withLauncher(...) transport/binaryPath/runnerPath не обязательны — launcher сам владеет обвязкой. Иначе все три обязательны. security() возвращает null, если секрет не был задан.
Низкоуровневый и расширяющий API
Для пулов, планировщиков и собственных бэкендов. Thread построен ровно на этих частях — вы можете управлять ими напрямую.
Launcher (interface)
Flytachi\Winter\Thread\Launch\Launcher — стратегия порождения процесса на стороне родителя.
public function launch(LaunchSpec $spec): ProcessHandle| Параметр | Тип | Описание |
|---|---|---|
$spec |
LaunchSpec |
всё необходимое для запуска процесса |
Возвращает ProcessHandle. Выбрасывает ThreadException при сбое. Реализуйте его, чтобы запускать по SSH, в контейнере или на удалённом узле.
CliLauncher (final readonly class)
Flytachi\Winter\Thread\Launch\CliLauncher — по умолчанию, на основе proc_open.
new CliLauncher(
string $binaryPath,
string $runnerPath,
PayloadTransport $transport,
array $childEnv = [],
)| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$binaryPath |
string |
— | бинарник PHP CLI |
$runnerPath |
string |
— | скрипт wRunner |
$transport |
PayloadTransport |
— | как payload подготавливается/принимается |
$childEnv |
array<string,string> |
[] |
дополнительное окружение потомка (например, ['WINTER_THREAD_SECRET' => '…']) |
Собирает команду, полностью экранированную через escapeshellarg. Пустой $childEnv → потомок наследует окружение родителя; непустой → сливается поверх унаследованного окружения.
ProcessHandle (final class)
Flytachi\Winter\Thread\Launch\ProcessHandle — низкоуровневый примитив процесса, возвращаемый launcher’ом. Изменяемый; отслеживает состояние процесса/pipe’ов/кода возврата. Не конструируется напрямую — получается из Launcher::launch().
| Метод | Возвращает | Описание |
|---|---|---|
getPid() |
int |
PID процесса |
isAlive() |
bool |
выполняется ли процесс сейчас? |
join(int $timeout = 0) |
?int |
блокирует до завершения; код возврата, null по таймауту (секунды) или записанный код / -1, если ресурс уже пропал |
reap() |
bool |
неблокирующий; true, если завершён (и пожат) |
detach() |
void |
прекратить отслеживание; закрывает pipe’ы, без proc_close, без очистки транспорта (ей владеет потомок) |
getExitCode() |
?int |
код возврата после пожинания; null после detach() |
readOutput() |
string |
неблокирующий STDOUT; '', если нет pipe вывода |
readError() |
string |
неблокирующий STDERR; '', если нет pipe вывода |
signal(int $signal) |
bool |
posix_kill(pid, signal) — только если жив; иначе false |
reap(), detach() и __destruct() неблокирующие на живом процессе; блокирующий proc_close выполняется только на уже мёртвом процессе. __destruct() пожинает завершённый процесс либо отсоединяет ещё работающий — он никогда не блокирует родителя.
LaunchSpec (final readonly class)
Flytachi\Winter\Thread\LaunchSpec — неизменяемый набор всех параметров запуска. Постройте его напрямую, чтобы управлять launcher’ом без Thread.
new LaunchSpec(
string $payload,
string $namespace = '',
string $name = 'anonymous',
?string $tag = null,
array $arguments = [],
bool $debug = false,
?string $output = '/dev/null',
bool $detached = false,
)| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$payload |
string |
— | уже сериализованный Runnable |
$namespace |
string |
'' |
группировка для заголовка процесса |
$name |
string |
'anonymous' |
имя для заголовка процесса |
$tag |
?string |
null |
тег экземпляра |
$arguments |
array<string, scalar|null> |
[] |
передаётся задаче как --arg-* |
$debug |
bool |
false |
отчёт об ошибках на стороне потомка |
$output |
?string |
'/dev/null' |
'/dev/null' | путь к файлу | null (pipe родителю) |
$detached |
bool |
false |
демонизировать потомка |
Все свойства публичные и readonly.
PayloadTransport (interface)
Flytachi\Winter\Thread\Payload\PayloadTransport — доставляет сериализованный payload через границу процессов в две части плюс очистка.
| Метод | Возвращает | Выполняется в | Описание |
|---|---|---|---|
stage(string $payload) |
StagedPayload |
родитель | подготовить доставку (спецификация fd-0, аргументы CLI, ссылка для очистки) |
receive(array $options) |
string |
потомок | прочитать payload обратно (STDIN или shm через shmkey) |
cleanup(StagedPayload $staged) |
void |
родитель | освободить подготовленные ресурсы; безопасно, даже если они уже пропали |
$options в receive() — это array<string, mixed> — разобранные опции CLI потомка.
Кастомным транспортам нужен подходящий runner
Стандартный AdaptiveRunner принимает только из STDIN или shm — он не вызывает receive() кастомного транспорта. Поэтому транспорт, доставляющий вне полосы (Redis/TCP/FIFO), также требует подходящего runner’а на стороне потомка. См. Swoole и доставка payload.
PipeTransport
…\Payload\PipeTransport — payload через pipe stdin потомка (записывается после запуска). Без расширений. Не безопасен для Swoole (использует pipe fd). По умолчанию в обычном CLI.
TempFileTransport
…\Payload\TempFileTransport — payload через временный файл 0600, помещённый на stdin, удаляемый (unlink) сразу после запуска (потомок сохраняет свой fd). Без расширений. Безопасен для Swoole. Выбрасывает ThreadException, если временный файл не удаётся создать/записать.
ShmTransport
…\Payload\ShmTransport — payload через сегмент System V разделяемой памяти 0600; целочисленный ключ передаётся как --shmkey, а потомок читает, затем удаляет его. Без диска, безопасен для Swoole. Требует ext-shmop — выбрасывает ThreadException ("ShmTransport requires ext-shmop.") как в stage(), так и в receive(), если расширения нет, а также при сбое выделения.
StagedPayload (final readonly class)
Flytachi\Winter\Thread\Payload\StagedPayload — результат stage(); launcher читает его обобщённо.
new StagedPayload(
array $stdinSpec,
array $cliArgs = [],
?string $pipePayload = null,
?string $unlinkAfterOpen = null,
mixed $ref = null,
)| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$stdinSpec |
array<int,string> |
— | дескриптор fd-0 для proc_open (['pipe','r'] или ['file',$path,'r']) |
$cliArgs |
array<int,string> |
[] |
дополнительные, уже безопасные аргументы запуска (например, ['--shmkey=123']) |
$pipePayload |
?string |
null |
записывается в pipe после запуска (транспорт pipe) |
$unlinkAfterOpen |
?string |
null |
удаляется (unlink) после запуска, когда потомок держит свой fd (транспорт temp-file) |
$ref |
mixed |
null |
непрозрачный дескриптор очистки (путь temp / ключ shm), передаваемый в cleanup() |
Runner (interface)
Flytachi\Winter\Thread\Runner\Runner — стратегия исполнения на стороне потомка.
public function execute(array $options): int| Параметр | Тип | Описание |
|---|---|---|
$options |
array<string, mixed> |
разобранные опции CLI wRunner (namespace, name, tag, debug, detach, shmkey) |
Возвращает int — код возврата процесса (0 — успех, ненулевой — сбой).
AdaptiveRunner (final readonly class)
Flytachi\Winter\Thread\Runner\AdaptiveRunner — стандартный runner на стороне потомка (управляется wRunner). Он зависит только от провайдера безопасности — не от Engine — так что обе стороны остаются независимыми.
new AdaptiveRunner(
?DefaultSecurityProvider $security = null,
mixed $errStream = null,
)| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$security |
?DefaultSecurityProvider |
null |
проверяет подпись payload; постройте его из того же секрета, которым подписал родитель (null = без подписи). wRunner строит его из WINTER_THREAD_SECRET |
$errStream |
resource|null |
null |
куда пишутся диагностические сообщения; по умолчанию STDERR (внедряемо для тестов) |
Ход execute(): принять payload (--shmkey → shm, иначе STDIN) → проверить + десериализовать через opis/closure (отклоняя пустые, не-Runnable или неподписанные/подделанные payload’ы ненулевым кодом возврата) → опционально fork+setsid (detached) → установить заголовок процесса → run() → код возврата.
Замена runner'а
Чтобы использовать свой Runner, вы заменяете bootstrap потомка: напишите собственный скрипт вроде wRunner, который конструирует ваш runner (обычно запускается кастомным Launcher). Для runner’а нет шва bindEngine — по замыслу сторона потомка независима от Engine родителя. См. Жизненный цикл runner’а.
Signal (final class)
Flytachi\Winter\Thread\Signal — статические POSIX-хелперы по сырому PID. isProcessRunning() трактует зомби (состояние Z) как не выполняющиеся, как на Linux (/proc/<pid>/status), так и на macOS (ps).
| Метод | Возвращает | Описание |
|---|---|---|
isProcessRunning(int $pid) |
bool |
существует ли живой (не-зомби) процесс с этим PID? |
interrupt(int $pid) |
bool |
отправить SIGINT (если выполняется) |
termination(int $pid) |
bool |
отправить SIGTERM (если выполняется) |
close(int $pid) |
bool |
отправить SIGHUP (если выполняется) |
kill(int $pid) |
bool |
отправить SIGKILL (если выполняется) |
wait(int $pid, int $timeout = 10) |
bool |
опрашивать, пока не пропадёт; false по таймауту (секунды) |
interruptAndWait(int $pid, int $timeout = 10) |
bool |
SIGINT, затем ожидание |
terminationAndWait(int $pid, int $timeout = 10) |
bool |
SIGTERM, затем ожидание |
closeAndWait(int $pid, int $timeout = 10) |
bool |
SIGHUP, затем ожидание |
Нет pause/resume в Signal
Signal покрывает только сигналы остановки/завершения. Приостановка/возобновление по сырому PID не предоставляются — используйте posix_kill($pid, SIGSTOP) / posix_kill($pid, SIGCONT) напрямую либо API Thread::pause()/resume().
PID reuse
Сырые PID подвержены переиспользованию PID (PID reuse) в ОС — PID может быть переназначен после завершения исходного процесса. Предпочитайте Thread/ProcessHandle для надёжного отслеживания; используйте Signal только со свежеполученными PID (например, самостоятельно сообщённый PID detached-воркера). См. PID reuse и сигналы.
ThreadException (class)
Flytachi\Winter\Thread\ThreadException extends \RuntimeException — единственный тип исключения библиотеки.
Выбрасывается при: сбое запуска (proc_open отклонён или процесс сразу умер), запуске уже работающего Thread, некорректной конфигурации (незаданная часть ManualEngine или ShmTransport без ext-shmop) и сбоях подготовки транспорта (временный файл / выделение shm). Ловите его вокруг start() и настройки движка.