folk-builder
Собирает PHP-расширение folk.so из Rust-кода. Folk — это application server для PHP, аналог Swoole/RoadRunner/FrankenPHP, но реализованный как нативное PHP-расширение на Rust.
Что это
folk-builder — CLI-утилита. Она генерирует Cargo-проект с выбранными плагинами и компилирует его в folk.so (cdylib). Результат — файл расширения, который PHP загружает через extension=folk.so.
Установка folk-builder
Через cargo (нужен Rust)
# Установить Rust если нет:
|
# Установить folk-builder:
Из исходников
Требования для сборки folk.so
- Rust 1.88+ —
rustup update - PHP 8.2+ с
php-configв PATH - clang/libclang — для bindgen (генерация FFI-биндингов к PHP)
- pkg-config — для поиска PHP headers
На Ubuntu/Debian:
На macOS (Homebrew):
Многопоточность (ZTS)
Для запуска нескольких worker-потоков (count > 1 в folk.toml) PHP должен быть собран с Thread Safety (ZTS):
- Docker: используйте образ
php:8.4-zts— ZTS уже включён - Ubuntu:
apt install php8.4-zts(если есть в репо) - Из исходников:
./configure --enable-zts && make && make install - Проверить:
php -r "echo PHP_ZTS;"— должно вывести1
С NTS (обычным) PHP Folk работает, но только с одним worker-потоком.
Сборка расширения
1. Создать конфиг сборки
Файл folk.build.toml:
[]
= "folk" # Имя расширения (-> folk.so)
[[]]
= "folk-plugin-http" # HTTP-сервер (axum + hyper + tokio)
= "0.2"
= "http"
2. Собрать
Результат: файл folk.so в текущей директории.
3. Проверить
# folk-ext 0.2.0
Запуск Folk-сервера
Конфиг сервера
Файл folk.toml:
[]
= "server.php" # PHP-скрипт воркера
= 4 # Кол-во worker-потоков (нужен ZTS)
= 100000 # Рестарт после N запросов
[]
= "0.0.0.0:8080"
[]
= "warn" # trace, debug, info, warn, error
Простой HTTP-сервер (без фреймворка)
Запуск:
С Laravel
Файл server.php:
# Сервер слушает на :8080
Docker (рекомендуемый способ)
Не нужно ставить Rust и PHP ZTS локально — всё собирается в Docker.
Продакшен (всё в образе)
# === Сборка folk.so ===
FROM php:8.4-zts AS builder
# Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain 1.88.0
ENV PATH="/root/.cargo/bin:${PATH}"
# Системные зависимости для сборки
RUN apt-get update && apt-get install -y \
pkg-config libclang-dev clang protobuf-compiler \
&& rm -rf /var/lib/apt/lists/*
# Собираем folk-builder и folk.so
WORKDIR /build
RUN cargo install folk-builder
COPY folk.build.toml folk.build.toml
RUN folk-builder build --config folk.build.toml --output-dir /build/
# === Runtime ===
FROM php:8.4-zts
RUN apt-get update && apt-get install -y \
unzip curl sqlite3 \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& rm -rf /var/lib/apt/lists/*
# folk.so
COPY --from=builder /build/folk.so /usr/local/lib/php/extensions/folk.so
RUN echo "extension=/usr/local/lib/php/extensions/folk.so" > /usr/local/etc/php/conf.d/folk.ini
# Приложение
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY . .
RUN composer install --no-dev --optimize-autoloader
EXPOSE 8080
CMD ["php", "server.php"]
Локальная разработка (код через volume)
Для разработки код не копируется в образ — монтируется через volume. Образ содержит только runtime + folk.so.
docker/app.Dockerfile:
# Stage 1: Build folk.so
FROM php:8.4-zts AS builder
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.88.0
ENV PATH="/root/.cargo/bin:${PATH}"
RUN apt-get update && apt-get install -y \
pkg-config libclang-dev clang protobuf-compiler \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
RUN cargo install folk-builder
COPY folk.build.toml folk.build.toml
RUN folk-builder build --config folk.build.toml --output-dir /build/
# Stage 2: PHP ZTS runtime
FROM php:8.4-zts
RUN apt-get update && apt-get install -y \
unzip curl sqlite3 \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& rm -rf /var/lib/apt/lists/*
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY --from=builder /build/folk.so /usr/local/lib/php/extensions/folk.so
RUN echo "extension=/usr/local/lib/php/extensions/folk.so" > /usr/local/etc/php/conf.d/folk.ini
WORKDIR /app
EXPOSE 8080
CMD ["php", "server.php"]
compose.yaml:
services:
app:
build:
context: .
dockerfile: docker/app.Dockerfile
ports:
- "8080:8080"
- "9090:9090"
working_dir: /app
environment:
- APP_ENV=local
- APP_DEBUG=true
- DB_CONNECTION=sqlite
- REDIS_HOST=redis
- CACHE_STORE=redis
- SESSION_DRIVER=redis
- QUEUE_CONNECTION=redis
- RUST_LOG=info
volumes:
- ./:/app
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"
Запуск:
# Собрать образ (folk.so компилируется один раз)
# Установить PHP-зависимости (выполняется внутри контейнера)
# Запустить
Код приложения (app/, routes/, config/, resources/) монтируется через volume — изменения применяются при рестарте воркера (по max_jobs или docker compose restart app).
folk.build.toml (все плагины):
[]
= "folk"
[[]]
= "folk-plugin-http"
= "0.2"
= "http"
[[]]
= "folk-plugin-jobs"
= "0.2"
= "jobs"
[[]]
= "folk-plugin-grpc"
= "0.2"
= "grpc"
[[]]
= "folk-plugin-metrics"
= "0.2"
= "metrics"
[[]]
= "folk-plugin-process"
= "0.2"
= "process"
folk.toml:
[]
= "10s"
[]
= "server.php"
= 4
= 100000
[]
= "0.0.0.0:8080"
[]
= "memory"
[[]]
= "default"
= 2
[]
= "0.0.0.0:9090"
[]
= "info"
= "text"
Конфиг сборки — справочник
[build]
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
output |
string | да | Имя расширения (без .so) |
folk_ext_path |
string | нет | Путь к локальному folk-ext (для разработки) |
folk_api_path |
string | нет | Путь к локальному folk-api (для разработки) |
[[plugin]]
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
crate_name |
string | да | Имя Rust-крейта |
version |
string | нет | Версия с crates.io (по умолчанию "0.2") |
path |
string | нет | Локальный путь (вместо crates.io) |
git |
string | нет | Git URL (вместо crates.io) |
config_key |
string | да | Ключ секции в folk.toml |
Доступные плагины
| Плагин | Крейт | Версия | Описание |
|---|---|---|---|
| HTTP | folk-plugin-http |
0.2 | HTTP/1.1 сервер (axum) |
| gRPC | folk-plugin-grpc |
0.2 | gRPC сервер (tonic) |
| Jobs | folk-plugin-jobs |
0.2 | Очередь фоновых задач (memory/redis) |
| Metrics | folk-plugin-metrics |
0.2 | Prometheus /metrics и /health |
| Process | folk-plugin-process |
0.2 | Supervisor сайдкар-процессов |
Как это работает
PHP загружает folk.so
|
+-- Main thread (worker #1)
| Rust вызывает PHP handler напрямую через call_user_function
| Данные передаются как PHP-массивы (zval), без JSON
|
+-- folk-tokio thread
| axum HTTP-сервер, пул воркеров, реестр плагинов
|
+-- ZTS workers #2..N (если count > 1 и PHP собран с ZTS)
Каждый — отдельный PHP-контекст со своим Zend Engine
Производительность
Docker, Mac (OrbStack), 4 ZTS workers:
| Нагрузка | Req/sec |
|---|---|
Raw JSON ({"status":"ok"}) |
~55,000 |
| Laravel Livewire starter | ~4,200 |
Стабильно держит 500 concurrent connections без socket errors.
Лицензия
MIT