High-performance PHP 8.3+ API framework — boot once, serve forever.
QuillPHP is a binary-native API framework built for extreme low-latency environments. The key insight is simple: PHP never touches a socket.
The native Quill Core (Rust + Axum + Tokio) owns the entire I/O stack — TCP connections, route matching, DTO validation, and response serialisation. PHP is woken up only to run your handler, then goes back to polling. By strictly separating a one-time Boot Phase from a zero-overhead Hot Path, Quill reaches throughput that rivals compiled languages without leaving PHP.
| Framework | Throughput (req/s) | Avg Latency | Notes |
|---|---|---|---|
| Actix-web 4 (Rust) | ~450,000 | ~0.22 ms | TFB R22 JSON, 4-core¹ |
| Axum 0.7 (Rust / Tokio) | ~330,000 | ~0.30 ms | TFB R22 JSON, 4-core¹ |
| Go Fiber v2 (fasthttp) | ~220,000 | ~0.45 ms | TFB R22 JSON, 4-core¹ |
| QuillPHP (Native) | 133,627 | 1.16 ms | Direct measurement² |
| Go net/http (stdlib) | ~115,000 | ~0.87 ms | TFB R22 JSON, 4-core¹ |
| Node.js Fastify v4 | ~68,000 | ~1.47 ms | TFB R22 JSON, 4-core¹ |
| FrankenPHP (worker, NTS+JIT) | ~30,000 | ~3.33 ms | Estimated³ |
| Node.js Express v4 | ~18,000 | ~5.56 ms | TFB R22 JSON, 4-core¹ |
| FastAPI + Uvicorn (4 workers) | ~11,000 | ~9.09 ms | TFB R22 JSON, 4-core¹ |
| Laravel Octane (Swoole, bare) | ~10,000 | ~10.0 ms | Bare route, no middleware⁴ |
¹ TFB R22 extrapolated — TechEmpower Round 22 JSON Serialization results (48-core AMD EPYC 7R13, 512 connections) scaled proportionally to 4-core equivalent for fair comparison. Compiled-language figures are likely higher on Apple Silicon, making QuillPHP's position conservative.
² Direct measurement —
wrk -t4 -c100 -d10s,QUILL_WORKERS=4, Apple M-series. PHP never touches the socket; Axum/Tokio owns all I/O.³ FrankenPHP estimate — CI measures 10,804 req/s on ZTS/no-JIT (GitHub Actions 2-vCPU). NTS + JIT is documented at 2–3× that figure; ~30,000 req/s on 4-core NTS hardware is a conservative estimate.
⁴ Laravel Octane — Bare
Route::get('/hello', fn() => [...])with no sessions, DB, or auth middleware. A defaultlaravel newskeleton measures ~354 req/s on the same runner.
- Axum / Tokio HTTP Server — All TCP I/O runs inside a dedicated single-threaded Tokio runtime per worker, fully bypassing PHP's process model.
- matchit Radix Trie Router — Routes are compiled into a native radix trie at boot; every request dispatches in O(log n) with zero PHP involvement.
- Zero-Reflection Hot Path — Handler parameter maps are built once at boot via reflection and cached; the hot path does a single array lookup per argument.
- Native DTO Validation — Schema checks run inside the Rust
ValidatorRegistrybefore PHP is polled — invalid requests are rejected with a 400 without touching userland. - Multi-Worker via
pcntl_fork— The TCP port is pre-bound once, then forked N times. Each worker owns an independent Rust heap with no shared state. - sonic-rs SIMD JSON — JSON compaction and encoding accelerated by
sonic-rsacross the FFI boundary. - OpenAPI 3.0 — Automatic Swagger UI generation directly from your route and DTO definitions.
Quill enforces a hard boundary between the Boot Phase (reflection, compilation, registration) and the Hot Path (pure dispatch). The native core owns all I/O; PHP only runs your business logic.
Routes are compiled into a native manifest and the TCP port is pre-bound before pcntl_fork. Each worker independently re-initialises its Rust heap so there is zero shared state across processes.
flowchart TD
A["routes.php"] -->|"$app->get / post / ..."| B["App::boot()"]
B -->|"Router::compile()"| C["Route Manifest JSON"]
B -->|"Validator::register()"| E["DTO Schema JSON"]
C -->|"FFI → quill_router_build()"| RT[(matchit\nradix trie)]
E -->|"FFI → quill_validator_register()"| VL[(ValidatorRegistry)]
B -->|"FFI → quill_server_prebind(port)"| Sock[[Shared Socket fd]]
Sock -.->|"dup(2) per worker"| W1 & W2 & WN
subgraph W1 ["Worker 1 — parent process"]
direction LR
QC1["Quill Core\n(Axum / Tokio)"] <-->|"FFI bridge"| PH1[PHP Poll Loop]
end
subgraph W2 ["Worker 2 — pcntl_fork"]
direction LR
QC2["Quill Core\n(Axum / Tokio)"] <-->|"FFI bridge"| PH2[PHP Poll Loop]
end
subgraph WN ["Worker N — pcntl_fork"]
direction LR
QCN["Quill Core\n(Axum / Tokio)"] <-->|"FFI bridge"| PHN[PHP Poll Loop]
end
sequenceDiagram
participant C as Client
participant QC as Quill Core (Axum / Tokio)
participant RT as matchit Router
participant VL as ValidatorRegistry
participant PL as PHP Poll Loop
participant RM as RouteMatch
participant H as Your Handler
C->>+QC: HTTP Request
QC->>RT: match_route(method, path)
RT-->>QC: { handler_id, params, dto_class }
opt dto_class present
QC->>VL: validate(body_bytes)
VL-->>QC: typed data —or— 400 Bad Request
end
QC->>PL: quill_server_poll() → PendingRequest
PL->>RM: RouteMatch::execute($request)
RM->>RM: resolve args from param cache
RM->>H: $handler(...$args)
H-->>RM: array | HttpResponse
RM-->>PL: result
PL->>QC: quill_server_respond(id, json)
QC->>QC: parse { status, headers, body }
QC-->>-C: HTTP Response
Each worker's param cache is built once at boot via reflection and never touched again — zero reflection on the hot path.
composer create-project quillphp/quill my-api
cd my-api// routes.php
use Handlers\User\ListUsersAction;
use Handlers\User\CreateUserAction;
/** @var \Quill\App $app */
// Simple closure — zero dependencies
$app->get('/hello', fn() => ['message' => 'hello', 'status' => 'ok']);
// Class-based handler — JIT-friendly, stable param-cache key
$app->get('/users', [ListUsersAction::class, '__invoke']);
$app->post('/users', [CreateUserAction::class, '__invoke']); // auto-validates DTO# Single worker
php -d ffi.enable=on bin/quill serve
# Multi-worker (recommended for production)
QUILL_WORKERS=4 php -d ffi.enable=on bin/quill serve --port=8080- Architecture — Boot phase, hot path, and the FFI bridge in detail.
- Routing — Verb mapping, groups, resource routes, and path parameters.
- Validation — DTOs, PHP attributes, and native schema validation.
- Benchmarks — Methodology, hardware specs, and full comparison results.
We welcome contributions! Please see our Contributing Guide for local setup instructions.
QuillPHP is open-source software licensed under the MIT License.