# folk-builder
Generates and compiles a PHP extension (`folk.so`) that runs the Folk application server inside PHP.
Folk replaces Swoole/RoadRunner/FrankenPHP: your PHP process loads `folk.so`, which embeds a high-performance HTTP server (axum/hyper + tokio) with ZTS multi-worker threads.
## Quick Start
### 1. Install folk-builder
```bash
cargo install folk-builder
```
Or build from source:
```bash
git clone https://github.com/Folk-Project/folk-builder
cd folk-builder
cargo build --release
# Binary at target/release/folk-builder
```
### 2. Create build config
Create `folk.build.toml`:
```toml
[build]
output = "folk" # Extension name (produces folk.so)
[[plugin]]
crate_name = "folk-plugin-http" # HTTP server plugin
version = "0.2"
config_key = "http"
```
### 3. Build the extension
```bash
folk-builder build --config folk.build.toml --output-dir .
```
This generates a Cargo project, compiles it, and produces `folk.so`.
Requirements:
- Rust 1.88+
- PHP 8.2+ (NTS or ZTS) with `php-config` in PATH
- For multi-worker: PHP must be compiled with ZTS (`--enable-zts`)
### 4. Load and run
Create `folk.toml` (server config):
```toml
[workers]
script = "server.php"
count = 4 # Number of worker threads (requires ZTS PHP)
max_jobs = 100000
[http]
listen = "0.0.0.0:8080"
[log]
filter = "warn"
```
Create `server.php`:
```php
<?php
require __DIR__ . '/vendor/autoload.php';
// Start the Folk server (non-blocking).
if (!folk_is_worker_thread()) {
$server = new Folk\Server('folk.toml');
$server->start();
}
// Create worker loop and register handlers.
$loop = new \Folk\Sdk\Worker\WorkerLoop();
$loop->registerHttpHandler(new MyHttpHandler());
$loop->run();
```
Run:
```bash
php -d extension=./folk.so server.php
```
## Build Config Reference
### `[build]`
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `output` | string | yes | Extension name (without `.so`) |
| `folk_ext_path` | string | no | Local path to `folk-ext` crate (for development) |
| `folk_api_path` | string | no | Local path to `folk-api` crate (for development) |
### `[[plugin]]`
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `crate_name` | string | yes | Rust crate name |
| `version` | string | no | Version (default: `"0.2"`) |
| `path` | string | no | Local path (overrides version) |
| `git` | string | no | Git URL (overrides version) |
| `config_key` | string | yes | Key in `folk.toml` for plugin config |
### Available plugins
| Plugin | Crate | Description |
|--------|-------|-------------|
| HTTP | `folk-plugin-http` | HTTP/1.1 server (axum/hyper) |
| gRPC | `folk-plugin-grpc` | gRPC server |
| Jobs | `folk-plugin-jobs` | Background job queue |
| Metrics | `folk-plugin-metrics` | Prometheus metrics endpoint |
| Process | `folk-plugin-process` | Process management signals |
## Docker
For production, build `folk.so` in a multi-stage Docker image:
```dockerfile
FROM php:8.3-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 && rm -rf /var/lib/apt/lists/*
WORKDIR /build
COPY folk-builder/ /build/folk-builder/
RUN cd /build/folk-builder && cargo build --release
RUN cat > /build/folk.build.toml << 'TOML'
[build]
output = "folk"
[[plugin]]
crate_name = "folk-plugin-http"
version = "0.2"
config_key = "http"
TOML
RUN /build/folk-builder/target/release/folk-builder build \
--config /build/folk.build.toml --output-dir /build/
# --- Runtime ---
FROM php:8.3-zts
COPY --from=builder /build/folk.so /usr/local/lib/php/extensions/folk.so
RUN echo "extension=folk.so" > /usr/local/etc/php/conf.d/folk.ini
```
## Architecture
```
PHP loads folk.so
|
+-- Main thread (worker #1)
| folk_worker_run() -> Rust dispatch loop
| recv Value -> value_to_zval -> call_user_function -> zval_to_value
|
+-- folk-tokio thread
| axum HTTP server, worker pool, plugin registry
|
+-- ZTS workers #2..N (requires ZTS PHP)
ts_resource -> php_request_startup -> execute worker script
```
Zero JSON serialization. Data passes as direct zval arrays between Rust and PHP.
## Performance
Raw JSON, Docker, 2 CPU:
| Workers | Req/sec |
|---------|---------|
| 1 | ~27,000 |
| 4 | ~55,000 |
Laravel, Docker, 2 CPU, 4 workers: ~3,600 rps
## License
MIT