{"id":46668524,"url":"https://github.com/oxphp/oxphp","last_synced_at":"2026-04-22T00:01:50.564Z","repository":{"id":338688674,"uuid":"1152153919","full_name":"oxphp/oxphp","owner":"oxphp","description":"Async PHP application server written in Rust. Replaces nginx + PHP-FPM with a single binary.","archived":false,"fork":false,"pushed_at":"2026-04-17T19:33:08.000Z","size":2140,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-17T20:33:27.832Z","etag":null,"topics":["application-server","async","cloud","docker","http","http-server","http2","hyper","kubernetes","php","php-fpm-alternative","php8","rust","sapi","tokio","zts"],"latest_commit_sha":null,"homepage":"https://oxphp.dev","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oxphp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":".github/supported-versions.yml","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-07T12:54:41.000Z","updated_at":"2026-04-17T19:33:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/oxphp/oxphp","commit_stats":null,"previous_names":["oxphp/oxphp"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/oxphp/oxphp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxphp%2Foxphp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxphp%2Foxphp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxphp%2Foxphp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxphp%2Foxphp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxphp","download_url":"https://codeload.github.com/oxphp/oxphp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxphp%2Foxphp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32115216,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-21T11:25:29.218Z","status":"ssl_error","status_checked_at":"2026-04-21T11:25:28.499Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-server","async","cloud","docker","http","http-server","http2","hyper","kubernetes","php","php-fpm-alternative","php8","rust","sapi","tokio","zts"],"created_at":"2026-03-08T21:10:35.687Z","updated_at":"2026-04-22T00:01:50.551Z","avatar_url":"https://github.com/oxphp.png","language":"Rust","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.svg\" alt=\"OxPHP\" width=\"300\"\u003e\n\u003c/p\u003e\n\n\u003ch3 align=\"center\"\u003eMultithreaded PHP application server built for cloud-native infrastructure.\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n  OxPHP is an asynchronous PHP application server written in Rust —\u003cbr\u003e\n  built for production workloads that demand low latency, high concurrency, and zero-config observability.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eEnglish\u003c/b\u003e · \u003ca href=\"README.ru.md\"\u003eРусский\u003c/a\u003e · \u003ca href=\"README.zh.md\"\u003e中文\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Documents: \u003ca href=\"docs/en/\"\u003eEnglish\u003c/a\u003e · \u003ca href=\"docs/ru/\"\u003eРусский\u003c/a\u003e · \u003ca href=\"docs/zh/\"\u003e中文\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e · \u003ca href=\"#why-oxphp\"\u003eWhy OxPHP\u003c/a\u003e · \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e · \u003ca href=\"#configuration\"\u003eConfiguration\u003c/a\u003e · \u003ca href=\"#roadmap\"\u003eRoadmap\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Rust\" src=\"https://img.shields.io/badge/rust-powered-orange\"\u003e\n  \u003cimg alt=\"PHP\" src=\"https://img.shields.io/badge/php-8.4-blue\"\u003e\n  \u003cimg alt=\"License\" src=\"https://img.shields.io/github/license/oxphp/oxphp\"\u003e\n  \u003cimg alt=\"Release\" src=\"https://img.shields.io/github/v/release/oxphp/oxphp\"\u003e\n  \u003cimg alt=\"Stars\" src=\"https://img.shields.io/github/stars/oxphp/oxphp?style=flat\"\u003e\n  \u003cimg alt=\"Docker\" src=\"https://img.shields.io/badge/docker-ghcr.io-2496ED?logo=docker\u0026logoColor=white\"\u003e\n  \u003cimg alt=\"HTTP/2\" src=\"https://img.shields.io/badge/HTTP%2F2-supported-brightgreen\"\u003e\n  \u003cimg alt=\"TLS\" src=\"https://img.shields.io/badge/TLS-1.3-brightgreen\"\u003e\n\u003c/p\u003e\n\n---\n\n## Quick Start\n\nTwo lines. That's it.\n\n```dockerfile\nFROM ghcr.io/oxphp/oxphp:0.3.0\n\nCOPY --chown=www-data:www-data . /var/www/html/public\n```\n\n\u003e **Note:** By default, `DOCUMENT_ROOT` is `/var/www/html/public` — the snippet above copies your app directly into the document root. For Laravel, Symfony, Slim, or any project that already ships a `public/` subdirectory, use `COPY --chown=www-data:www-data . /var/www/html` instead: the framework's own `public/` lines up with the default `DOCUMENT_ROOT`.\n\n```bash\ndocker build -t my-app . \u0026\u0026 docker run -p 80:80 my-app\ncurl http://localhost/\n```\n\nNo nginx config. No PHP-FPM pool tuning. No process manager. Just your app.\n\nSee the full [Quick Start guide](docs/en/getting-started/quick-start.md) for more details.\n\n---\n\n## Why OxPHP?\n\nOxPHP replaces nginx + PHP-FPM with a single container. The server works out of the box — TLS, Brotli compression, rate limiting, Prometheus metrics, health checks, and structured JSON logs are configured via environment variables.\n\n| | nginx + PHP-FPM | FrankenPHP | RoadRunner | **OxPHP** |\n|---|---|---|---|---|\n| Language | C | Go + C | Go | **Rust + C** |\n| HTTP/2 | ✅ | ✅ | ✅ | ✅ |\n| HTTP/3 | ✅ | ✅ | ✅ experimental | 🔜 roadmap |\n| TLS 1.3 | ✅ | ✅ | ✅ | ✅ (rustls) |\n| Persistent worker state | ❌ | ✅ | ✅ | ✅ |\n| Backpressure / HTTP 529 | manual | ❌ | ❌ | ✅ built-in |\n| Prometheus metrics | plugin | built-in (Caddy admin) | built-in plugin | ✅ built-in |\n| Structured JSON logs | via `log_format` | ✅ | ✅ | ✅ built-in |\n| Per-IP rate limiting | built-in | community module | ❌ | ✅ built-in |\n| Custom error pages | ✅ (nginx config) | ✅ (Caddyfile) | ❌ | ✅ preloaded at startup |\n| HTTP 103 Early Hints | ✅ (v1.29+) | ✅ | ✅ | 🔜 roadmap |\n| Memory safety | ❌ (C) | partial (Go + cgo) | ✅ (Go, PHP isolated via IPC) | partial (Rust + C FFI) |\n| WebSocket server | ✅ (proxies) | ✅ (Mercure) | ✅ (centrifuge plugin) | ❌ |\n| Reverse proxy / upstream | ✅ (full-featured) | ✅ (Caddy) | ✅ | ❌ |\n| Native install (non-Docker) | apt/yum/brew/port | brew, static binary | brew, binary | roadmap |\n| Platforms (runtime) | Linux/BSD/Win/Mac | Linux/Mac/Win | Linux/Mac/Win | Linux only (glibc/musl) |\n| Supported PHP versions | 7.4–8.4 | 8.2–8.4 | 7.4–8.4 | 8.4 only (8.5 crashes with SIGBUS) |\n| License | BSD-2 / PHP License | Apache-2.0 | MIT | AGPL-3.0 |\n| Age / production track record | 20+ years | 2+ years | 7+ years | \u003c1 year |\n\nSee the full [documentation](docs/en/index.md) for details.\n\n---\n\n## Benchmarks\n\n\u003e Formal benchmarks are coming soon. We are working on a reproducible test suite covering req/s, latency (p50/p99), memory usage, and worker throughput under concurrent load.\n \n---\n\n## Features\n\n### PHP Runtime\n- **Native PHP execution** — PHP runs directly inside the server process, in a dedicated thread pool\n- **Full superglobals** support: `$_SERVER`, `$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `php://input` — see [Superglobals](docs/en/php/superglobals.md)\n- **HTTP Object API** — `oxphp_http_request()` returns a typed, lazy-loading request object with built-in JSON body parsing, content-detected MIME types for uploads, and a mutable attributes container for middleware — see [HTTP Request API](docs/en/php/request-api.md)\n- **Shared OPcache** across all workers — one worker compiles a file, every worker uses the cached bytecode — see [OPcache and JIT](docs/en/php/opcache.md)\n- **PHP extension functions** — `oxphp_*()` helpers for streaming, early response, async, tracing, and request access — see [PHP functions reference](docs/en/php/functions.md)\n- **Plugin system** with typed event dispatch, priority ordering, and PHP function registration\n- **Attribute-based decorators** — intercept function/method calls via PHP 8+ attributes with zero overhead on undecorated code; supports `TARGET_FUNCTION`, `TARGET_METHOD`, `TARGET_CLASS` — see [Decorators](docs/en/features/decorators.md)\n- **Crash isolation** — a fatal error in one request does not take down the server\n\n### Worker Model\n- **Worker mode** — persistent PHP processes that stay alive across requests; autoloaders, service containers, and DB connections are initialized once and reused — see [Worker mode](docs/en/features/worker-mode.md)\n- **Fiber multiplexing** — each worker handles multiple concurrent requests via PHP 8.4 Fibers; `oxphp_sleep()` and `oxphp_async_await()` yield the current fiber instead of blocking the worker thread — see [Fiber multiplexing](docs/en/features/fiber-multiplexing.md)\n- **Automatic recycling** by request count or memory threshold\n- **Worker health monitoring** — crashed workers are automatically detected and restarted\n- **Early response** via `oxphp_finish_request()` — send the response and keep running background work — see [Early response](docs/en/features/early-response.md)\n\n### Async Promises\nSee the full [Async promises guide](docs/en/features/async-promises.md).\n\n- **`oxphp_async()` / `oxphp_async_await()`** — dispatch closures to a dedicated thread pool for true parallel execution\n- **Portable serialization** for `use` variables, arguments, and return values — safe cross-thread binary transfer\n- Supported types: scalars, strings, arrays (nested). Resources and objects rejected with `E_WARNING`\n- **Exception \u0026 die() safety** — exceptions, `die()`, and `exit()` are caught and re-thrown as `OxPHP\\Async\\Exception`\n- **Timeout support** — per-task timeouts with `OxPHP\\Async\\TimeoutException`\n- **`oxphp_async_await_all()` / `oxphp_async_await_any()`** — batch and race primitives\n\n### Shared State (`OxPHP\\Shared\\*`)\nProcess-wide concurrent primitives that let PHP workers coordinate mutable state without Redis, Memcached, or APCu. Everything lives in-process — per-op cost is microseconds, not network round-trips. See the full [Shared state guide](docs/en/features/shared-state.md) and [observability reference](docs/en/operations/shared-observability.md).\n\n- **`Shared\\Counter`** — atomic int64 (`inc`, `dec`, `add`, `compareAndSet`) — see [Counter](docs/en/features/shared-counter.md)\n- **`Shared\\Flag`** — atomic bool with `compareAndSet` for one-shot transitions — see [Flag](docs/en/features/shared-flag.md)\n- **`Shared\\Once`** — run-once container with reentrancy-safe factory — see [Once](docs/en/features/shared-once.md)\n- **`Shared\\Mutex`** — poisoning mutex over a stored value, with reentrancy and cross-thread deadlock detection — see [Mutex](docs/en/features/shared-mutex.md)\n- **`Shared\\Channel`** — bounded MPMC queue, fiber-aware (blocking recv yields the current fiber) — see [Channel](docs/en/features/shared-channel.md)\n- **`Shared\\Map`** — concurrent string-keyed store with batched `setMany`/`getMany` and cycle-checked nested values — see [Map](docs/en/features/shared-map.md)\n- **`Shared\\Pool`** — bounded object pool with strict per-thread affinity, idle-timeout eviction, and chaos-reclaim on worker death — see [Pool](docs/en/features/shared-pool.md)\n- **Built-in observability** — `oxphp_shared_*` Prometheus counters and `/__ox_shared/{summary,entries,entry,preview,types,graph}` JSON endpoints on the internal port\n- **Refcount + lifecycle safety** — handles cannot outlive the registry entry; cycle detector rejects graphs that would leak memory\n- When you outgrow it, see [Migrating to an external store](docs/en/features/migrating-to-external-store.md)\n\n### HTTP \u0026 Networking\n- **HTTP/1.1 + HTTP/2** with automatic protocol detection (h2c)\n- **TLS 1.3** with ALPN — both HTTP/2 and HTTP/1.1 over TLS — see [TLS](docs/en/features/tls.md)\n- **3 routing modes** — Traditional (file mapping + always-on PATH_INFO), Framework (`index.php` rewrite with `PATH_INFO=$request_uri`), SPA (`index.html` for no-extension paths, hard 404 for missing assets). Each mode mirrors a familiar nginx `try_files` configuration — see [Routing](docs/en/features/routing.md)\n- **SSE streaming** via `Content-Type: text/event-stream` auto-detection or `oxphp_stream_flush()` — cooperative with fiber multiplexing — see [Server-Sent Events](docs/en/features/sse.md)\n- **Configurable timeouts** — header read, request, and keep-alive — see [Timeouts](docs/en/features/timeouts.md)\n\n### Performance\n- **LRU file cache** for static files (in-memory ≤1 MB, streaming for larger) — see [Static files](docs/en/features/static-files.md)\n- **HTTP caching** with ETag, Last-Modified, and 304 Not Modified\n- **Brotli compression** for text responses (256 B – 3 MB range) — see [Compression](docs/en/features/compression.md)\n- **mimalloc** allocator for lower allocation latency under contention\n- **Configurable HTTP server threads** — multi-threaded by default (CPU/2), tunable via `TOKIO_WORKERS`\n\n### Observability\nFull guide: [Distributed tracing](docs/en/features/distributed-tracing.md).\n\n- **W3C Trace Context** — automatic `traceparent`/`tracestate` propagation, `$_SERVER['OXPHP_TRACE_ID']` for PHP log correlation\n- **OpenTelemetry** — OTLP span export (gRPC/HTTP) with semantic conventions, configurable sampling, batch processing\n- **APM auto-instrumentation** — 33 internal PHP functions (PDO, mysqli, cURL, Redis, Memcached, file I/O) hooked at the engine level; every call becomes a span with zero code changes\n- **`#[OxPHP\\Tracing\\Trace]` decorator** — annotate any function or method with a PHP 8 attribute to create spans automatically\n- **PHP tracing SDK** — 10 `oxphp_trace_*()` functions for manual span creation, attributes, events, error recording, and trace context propagation\n- **Prometheus metrics** at `/metrics` — per-worker, zero dependencies — see [Metrics](docs/en/operations/metrics.md)\n- **Health check** at `/health` — ready for K8s readiness probes — see [Health checks](docs/en/operations/health-checks.md)\n- **Internal server** on a separate port for health, metrics, and runtime config — see [Internal server](docs/en/features/internal-server.md)\n- **Structured error logging** — PHP errors appear in the server log with `php_error_type`, `php_file`, `php_line` fields\n- **JSON access logging** with optional `trace_id`/`span_id` fields (levels: `all`, `error`, off via `ACCESS_LOG`) — see [Access logging](docs/en/features/access-logging.md)\n- **Request ID** generation + pass-through (`X-Request-ID`); trace-derived when OTel enabled — see [Request IDs](docs/en/features/request-ids.md)\n\n### Profiling (`plugin-profiler` feature)\n\nFull guide: [Profiling](docs/en/features/profiling.md).\n\n- **Per-request profile capture** — triggered by cookie (`OXPROF`), header (`X-OxPHP-Profile`), query (`?__oxprof=`), or statistical sampling (`PROFILER_SAMPLE_RATE`); constant-time token compare\n- **Four export formats** — xhprof (for xhgui), speedscope (for speedscope.app), pprof (Go tools / Pyroscope), collapsed (FlameGraph)\n- **Rich per-span data** — wall-time, CPU time, memory (start/end), events, attributes — nanosecond precision throughout\n- **PHP SDK** — 7 functions (`OxPHP\\Profile\\{start, stop, pause, resume, mark, metric, is_active}`) + 7 attributes (4 observer-filter: `#[Profile]` / `#[Exclude]` / `#[Sample]` / `#[Tag]`; 3 decorators: `#[Mark]` / `#[SlowThreshold]` / `#[MemoryThreshold]`)\n- **Shared tree with APM** — both plugins read one `Arc\u003cSpanTree\u003e`; no double collection; APM continues to export only explicit spans to OTel while the profiler keeps the full tree\n- **In-memory LRU + disk retention** — last `PROFILER_RETENTION_COUNT` runs always retrievable, token-bucket rate-limited writes, 5 s atomic-rename background trimmer\n- **HTTP push** — ship profiles to xhgui or any collector; 3× exponential backoff (100/200/400 ms) with 5 s wallclock cap; xhgui envelope auto-detect\n- **Internal HTTP routes** at `/__profiler/` — 8 endpoints (list / metadata / raw / speedscope redirect / DELETE / config / stats / landing) with optional bearer-token auth and path-traversal validation\n- **Prometheus metrics** — 6 counters + 1 gauge (runs, spans, bytes, disk drops, push failures, truncated, in-memory runs) via `/metrics`\n\n### Reliability \u0026 Operations\n- **Bounded request queue** with 529 backpressure when full\n- **Per-IP rate limiting** with `X-RateLimit-*` headers and 429 responses — see [Rate limiting](docs/en/features/rate-limiting.md)\n- **Custom error pages** — pre-loaded at startup, zero I/O on the hot path — see [Error pages](docs/en/features/error-pages.md)\n- **Graceful shutdown** — in-flight requests drain within `DRAIN_TIMEOUT_SECONDS` on SIGTERM/SIGINT — see [Graceful shutdown](docs/en/operations/graceful-shutdown.md)\n- **Path traversal protection** with symlink escape detection\n- **Trusted proxy support** — real client IP extraction from `Forwarded` (RFC 7239) and `X-Forwarded-*` headers with CIDR-based trust — see [Trusted proxies](docs/en/security/trusted-proxies.md)\n- **Dot-path blocking** — returns 404 for hidden files (`.env`, `.git/`) with `.well-known` exception (RFC 8615) — see [Dot-path blocking](docs/en/security/dot-path-blocking.md)\n- **Non-root container** execution as www-data (UID 82)\n\n---\n\n## Architecture\n\n```mermaid\nflowchart TD\n    Client([Client])\n    HTTP[\"Async HTTP server\u003cbr/\u003esingle- or multi-threaded\"]\n    Route{Route dispatch}\n    Static[\"Static file\u003cbr/\u003eLRU cache\"]\n    Queue[(\"Bounded queue\u003cbr/\u003e529 when full\")]\n    NF[\"404 Not Found\"]\n    Pool[\"Async pool\u003cbr/\u003eoxphp_async / oxphp_async_await\"]\n\n    Client --\u003e HTTP\n    HTTP --\u003e Route\n    Route --\u003e|static| Static\n    Route --\u003e|miss| NF\n    Route --\u003e|PHP| Queue\n    Queue --\u003e PhpWorkers\n    PhpWorkers -.-\u003e Pool\n    Pool --\u003e AsyncWorkers\n\n    subgraph PhpWorkers [PHP workers — dedicated OS threads]\n        direction BT\n        W1[Worker]\n        W2[Worker]\n        W3[Worker]\n    end\n\n    subgraph AsyncWorkers [Async workers — dedicated OS threads]\n        direction BT\n        A1[Worker]\n        A2[Worker]\n        A3[Worker]\n    end\n```\n\n- **Async HTTP server** — multi-threaded by default, tunable via `TOKIO_WORKERS`\n- **PHP worker pool** — each worker is a dedicated OS thread; a crash in one worker does not affect the others\n- Requests wait in a bounded queue between the HTTP server and the PHP workers; the queue returns 529 when full\n- **Async pool** — separate threads for `oxphp_async()` tasks, preventing slowdowns in the main worker pool\n- **Worker mode** — persistent PHP processes that stay alive between requests; autoloaders and DB connections are shared across all requests handled by that worker\n\n### Internal Server\n\nWhen `INTERNAL_ADDR` is set, a lightweight HTTP server starts on a separate port:\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /health` | JSON health status (uptime, requests, connections) |\n| `GET /metrics` | Prometheus text format metrics |\n| `GET /config` | JSON runtime configuration (TLS paths redacted) |\n\n### Tracing pipeline (`plugin-otel` + `plugin-apm`)\n\nAPM depends on OTel and shares its `TracerProvider` via the plugin service registry. Span collection happens on the PHP worker thread; OTLP export runs off the hot path via `tokio::spawn`.\n\n```mermaid\nflowchart LR\n    subgraph Tokio1 [\"Tokio thread — request start\"]\n        TC[\"Trace context handler\u003cbr/\u003e(priority -95)\u003cbr/\u003egenerates trace_id / span_id\"]\n        OTR[\"OtelRequestHandler (-80)\u003cbr/\u003erecords start_us,\u003cbr/\u003esets X-Request-ID\"]\n    end\n\n    subgraph PHP [\"PHP worker thread\"]\n        SDK[\"PHP tracing SDK\u003cbr/\u003eoxphp_trace_*()\"]\n        DEC[\"#[OxPHP\\\\Apm\\\\Trace]\u003cbr/\u003edecorator\"]\n        HOOKS[\"APM hooks (33 fn)\u003cbr/\u003ePDO · mysqli · cURL\u003cbr/\u003eRedis · Memcached · file I/O\"]\n        STACK[(\"SPAN_STACK\u003cbr/\u003ethread-local\")]\n        PHPERR[\"PHP errors\"]\n    end\n\n    subgraph Tokio2 [\"Tokio thread — request end\"]\n        OTC[\"OtelCompleteHandler\u003cbr/\u003ebuilds root server span\"]\n        APC[\"ApmCompleteHandler (-70)\u003cbr/\u003eparses child spans JSON,\u003cbr/\u003elinks to root span\"]\n    end\n\n    subgraph Export [\"Background export (tokio::spawn)\"]\n        BATCH[\"BatchSpanProcessor\u003cbr/\u003e(shared TracerProvider)\"]\n        OTLP[\"OTLP exporter\u003cbr/\u003egRPC :4317 / HTTP :4318\"]\n    end\n\n    TC --\u003e OTR\n    OTR --\u003e SDK\n    OTR --\u003e DEC\n    OTR --\u003e HOOKS\n    SDK --\u003e STACK\n    DEC --\u003e STACK\n    HOOKS --\u003e STACK\n    STACK --\u003e|Arc\u003cSpanTree\u003e via profile_tree| APC\n    PHPERR --\u003e|structured log| APC\n    OTR --\u003e OTC\n    OTC --\u003e BATCH\n    APC --\u003e BATCH\n    BATCH --\u003e OTLP\n```\n\n- **Trace context** is generated first (priority `-95`) when `TRACE_CONTEXT=true` (auto-enabled by OTel). OTel's request handler at `-80` records `start_us`; APM's handler runs at `-70`.\n- **Span collection is thread-local** — each PHP worker has its own `SPAN_STACK`. APM hooks, the `#[Trace]` decorator, and the `oxphp_trace_*()` SDK all push onto the same stack; child spans serialize to JSON at request end.\n- **Shared `TracerProvider`** — OTel registers `otel.provider` as a plugin service; APM fetches the same `Arc\u003cOnceLock\u003cTracerProvider\u003e\u003e` so both plugins export to the same batch processor.\n- **Off-hot-path export** — both complete handlers `tokio::spawn` OTLP export; the HTTP response is returned to the client before spans are sent.\n- **Provider lifecycle** — OTel initializes the `BatchSpanProcessor` in `on_ready()` (after the Tokio runtime starts). On shutdown, `force_flush()` + `shutdown()` drain pending spans.\n\n---\n\n## Configuration\n\nAll settings are via environment variables — no config files required.\n\n| Variable | Default | Description |\n|---|---|---|\n| `LISTEN_ADDR` | `0.0.0.0:80` | Address and port to bind |\n| `DOCUMENT_ROOT` | `/var/www/html/public` | Filesystem path to serve files from |\n| `INDEX_FILE` | *(unset)* | Routing mode: empty = Traditional, `*.php` = Framework, anything else = SPA |\n| `TOKIO_WORKERS` | `0` (CPU / 2, min 1) | HTTP server threads for handling connections; `0` = auto |\n| `EXECUTOR` | `sapi` | PHP executor: `sapi` (real PHP) or `stub` (test mode) |\n| `PHP_WORKERS` | `0` (CPU / 2, min 1) | Worker pool: `N` = fixed, `MIN:MAX` = dynamic, `0` = auto |\n| `PHP_WORKERS_IDLE_SECONDS` | `30` | Idle timeout before retiring a dynamic worker |\n| `QUEUE_CAPACITY` | `PHP_WORKERS * 128` | Max pending requests in the queue before the server returns 529 |\n| `DRAIN_TIMEOUT_SECONDS` | `30` | Graceful shutdown drain timeout |\n| `LOG_LEVEL` | `info` | Tracing verbosity: `error`, `warn`, `info`, `debug`, `trace` |\n| `INTERNAL_ADDR` | *(unset)* | Internal server for health/metrics/config (e.g. `0.0.0.0:9090`) |\n| `RATE_LIMIT` | `0` (off) | Max requests per IP per window |\n| `RATE_WINDOW_SECONDS` | `60` | Rate limit window in seconds |\n| `HEADER_TIMEOUT_SECONDS` | `5` | Header read timeout (Slowloris protection) |\n| `REQUEST_TIMEOUT_SECONDS` | `120` | Overall request timeout; 0 = disabled |\n| `TLS_CERT` | *(unset)* | Path to TLS certificate PEM file |\n| `TLS_KEY` | *(unset)* | Path to TLS private key PEM file |\n| `ERROR_PAGES_DIR` | *(unset)* | Directory with custom error pages (`{status}.html`) |\n| `STATIC_CACHE_TTL` | `30d` | Static file cache TTL (`30s`, `5m`, `2h`, `30d`, `1y`, `off`) |\n| `STATIC_CACHE` | *(on)* | Set to `off` to enable mtime revalidation on the in-memory content cache |\n| `COMPRESSION_LEVEL` | `4` | Brotli quality (0 = off, 1–11) |\n| `ACCESS_LOG` | *(off)* | Per-request JSON log: `all`, `error`, or unset |\n| `MAX_CONNECTIONS` | `10000` | Maximum concurrent connections |\n| `WORKER_FILE` | *(unset)* | Path to worker PHP script; enables persistent worker mode |\n| `WORKER_MAX_REQUESTS` | `0` (unlimited) | Max requests per worker before recycling |\n| `WORKER_MAX_MEMORY_MIB` | `0` (unlimited) | Max memory (MiB) per worker before recycling |\n| `SUPERGLOBALS_ENABLED` | `true` | Populate `$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `$_SERVER`; set `false` to rely solely on `oxphp_http_request()` |\n| `ASYNC_WORKERS` | `0` (disabled) | Dedicated async worker threads for `oxphp_async()` |\n| `ASYNC_QUEUE_CAPACITY` | `ASYNC_WORKERS * 64` | Max pending async tasks in the queue; tasks are rejected when full |\n| `TRACE_CONTEXT` | `false` | W3C Trace Context propagation (`traceparent`/`tracestate`). Auto-enabled when `OTEL_ENABLED=true` |\n| `TRUSTED_PROXIES` | *(unset)* | Trusted proxy CIDRs: `10.0.0.0/8,172.16.0.0/12` or `private` (all RFC-1918). Enables real client IP extraction from `Forwarded`/`X-Forwarded-*` headers |\n| `PHP_DENY_DIRS` | *(unset)* | Glob patterns of directories where PHP execution is blocked. Traditional mode only. Example: `/uploads/**,/cache/**` |\n| `PHP_DENY_FALLBACK` | `404` | HTTP status code (400–599) or path to a PHP fallback script. On match from `PHP_DENY_DIRS`, the response is the status (with optional custom HTML from `ERROR_PAGES_DIR`) or the fallback script runs with `OXPHP_DENIED_PATH` / `OXPHP_DENIED_PATTERN` in `$_SERVER` |\n\n### OpenTelemetry (`plugin-otel` feature)\n\n| Variable | Default | Description |\n|---|---|---|\n| `OTEL_ENABLED` | `false` | Enable span export. Implies `TRACE_CONTEXT=true` |\n| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4317` | OTLP collector endpoint |\n| `OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc` | Export protocol: `grpc` (port 4317) or `http/protobuf` (port 4318) |\n| `OTEL_EXPORTER_OTLP_TIMEOUT` | `10000` | Export timeout in milliseconds |\n| `OTEL_EXPORTER_OTLP_HEADERS` | *(unset)* | Auth headers for hosted backends (`key=value,key=value`) |\n| `OTEL_SERVICE_NAME` | `oxphp` | Service name in exported traces |\n| `OTEL_SERVICE_VERSION` | *(unset)* | Service version in exported traces |\n| `OTEL_RESOURCE_ATTRIBUTES` | *(unset)* | Resource attributes (`key=value,key=value`) |\n| `OTEL_TRACES_SAMPLER` | `parentbased_traceidratio` | Sampler: `always_on`, `always_off`, `traceidratio`, `parentbased_traceidratio` |\n| `OTEL_TRACES_SAMPLER_ARG` | `1.0` | Sampling ratio (0.0–1.0) |\n\n### APM (`plugin-apm` feature)\n\n| Variable | Default | Description |\n|---|---|---|\n| `OTEL_APM_ENABLED` | `false` | Enable APM: auto-instrumentation, error capture, PHP tracing SDK. Requires `OTEL_ENABLED=true` |\n| `OTEL_APM_SLOW_QUERY_MS` | `100` | Slow query threshold (ms). Queries above this get `oxphp.db.slow=true` |\n| `OTEL_APM_DB_CAPTURE_PARAMS_ENABLED` | `false` | Record bind parameters in `db.params` span attribute |\n\n### Shared State (`plugin-shared` feature)\n\n| Variable | Default | Description |\n|---|---|---|\n| `SHARED_ENABLED` | `true` | Master switch for the `OxPHP\\Shared\\*` layer |\n| `SHARED_MAX_ENTRIES` | `100000` | Max number of registry entries (counters + flags + maps + …) before `OutOfCapacityException` |\n| `SHARED_MAX_BYTES` | `1073741824` (1 GiB) | Soft cap on aggregate memory used by Shared\\* entries |\n| `SHARED_SOFT_LIMIT_RATIO` | `0.7` | Fraction of `MAX_*` at which `oxphp_shared_capacity_warn` fires |\n| `SHARED_LOCK_DIAGNOSTICS` | `warn` (release) / `strict` (debug) | Mutex deadlock detection: `off`, `warn` (log only), `strict` (break the cycle) |\n| `SHARED_CYCLE_DETECT_DEPTH` | `16` | Max BFS depth when checking nested-Shareable insertions for cycles |\n| `SHARED_CYCLE_DETECT_EDGES` | `10000` | Max edges walked per cycle check (guard against dense graphs) |\n| `SHARED_INTROSPECTION_ENABLED` | `true` | Toggle the `/__ox_shared/*` JSON endpoints on the internal server |\n| `SHARED_METRICS_ENABLED` | `true` | Toggle the `oxphp_shared_*` Prometheus series |\n| `SHARED_SHUTDOWN_TIMEOUT_SECONDS` | `5.0` | Max time to drain Channel/Pool entries on graceful shutdown |\n\n### Hardening legacy PHP apps\n\n`PHP_DENY_DIRS` locks down writable public subdirectories in Traditional routing mode — the typical attack surface for uploaded PHP shells (WordPress, older CMSes).\n\n```bash\n# Block PHP execution in writable public subdirs of a legacy PHP app.\nexport PHP_DENY_DIRS=/uploads/**,/cache/**,/tmp/**\nexport PHP_DENY_FALLBACK=403\n# Optional: pair with ERROR_PAGES_DIR=/var/errors for a custom 403.html\n```\n\n---\n\n## Build\n\n```bash\n# Host (without PHP — all tests pass, no PHP execution)\ncargo build --release\n\n# Docker (with PHP — full functionality)\ndocker compose build\n```\n\n### Run locally (static files only)\n\n```bash\nDOCUMENT_ROOT=./www/public ./target/release/oxphp\n```\n\n## Development\n\n```bash\n# Full verification (host)\ncargo fmt -- --check \u0026\u0026 cargo clippy --no-default-features -- -D warnings \u0026\u0026 cargo test --no-default-features\n\n# Docker smoke tests\ndocker compose build \u0026\u0026 docker compose up -d\ncurl http://localhost/\ncurl \"http://localhost/test_superglobals.php?foo=bar\"\ncurl -X POST -d \"key=value\" http://localhost/test_superglobals.php\ncurl -H \"Cookie: session=abc\" http://localhost/test_superglobals.php\n\n# Async promises\ncurl http://localhost/test_async.php\ncurl http://localhost/test_async_parallel.php\ncurl http://localhost/test_async_die.php\n\n# Internal server\nINTERNAL_ADDR=127.0.0.1:9090 ./target/release/oxphp \u0026\ncurl http://localhost:9090/health\ncurl http://localhost:9090/metrics\n```\n\n---\n\n## Roadmap\n\n\u003e Items are not ordered by priority. Presence on this list does not guarantee implementation.\n\n| Feature | Description |\n|---|---|\n| **PHP 8.5** | Support for PHP 8.5 |\n| ~~**Trace Context (W3C)**~~ | ✅ Implemented — automatic propagation of `traceparent` / `tracestate` headers (W3C spec), enabled via `TRACE_CONTEXT=true` |\n| ~~**OpenTelemetry**~~  | ✅ Implemented — OTLP trace export via `plugin-otel` feature, W3C context propagation, per-request spans with standard semantic conventions |\n| ~~**APM \u0026 Auto-Instrumentation**~~ | ✅ Implemented — `plugin-apm` feature: automatic tracing of 33 internal PHP functions (PDO, mysqli, cURL, Redis, Memcached, file I/O), `#[OxPHP\\Tracing\\Trace]` decorator, 10 `oxphp_trace_*()` SDK functions, PHP error capture |\n| **Custom Metrics** | PHP API for registering application-defined Prometheus metrics from userland code |\n| ~~**Built-in PHP Profiler**~~ | ✅ Implemented — `plugin-profiler` feature: per-request profiling with xhprof/speedscope/pprof/collapsed formats, PHP SDK, attribute triggers, in-memory LRU + disk retention, HTTP push to xhgui, `/__profiler/` internal routes, Prometheus metrics — see [Profiling](docs/en/features/profiling.md) |\n| **Dockerfile.bookworm** | Official Debian Bookworm-based image as an alternative to Alpine |\n| **Non-Docker Install** | Native installation via system package managers (apt, brew, etc.) |\n| **HTTP/3** | QUIC-based HTTP/3 support |\n| **HTTP 103 Early Hints** | Send `103 Early Hints` responses to allow clients to preload resources before the final response |\n| **Ecosystem Plugins** | Expanded plugin system: more lifecycle hooks, richer PHP API, and documentation for third-party plugin authors |\n| ~~**Shared Async Runtime**~~ | ✅ Implemented — the same async runtime powers both the HTTP server and `oxphp_async()` / `oxphp_async_await()` with timeouts, result delivery, and race coordination |\n| **Database Connection Pool** | Built-in connection pooling via `sqlx`, reducing per-request connection overhead |\n| **gRPC Server** | *(speculative)* An alternative server mode — gRPC instead of HTTP; very uncertain, may not happen |\n| ~~**Promise API**~~ | ✅ Implemented — `oxphp_async()` / `oxphp_async_await()` with dedicated thread pool, portable serialization, and exception safety |\n| ~~**Fiber Multiplexing**~~ | ✅ Implemented — each worker handles multiple concurrent requests via PHP 8.4 Fibers; `oxphp_sleep()` / `oxphp_usleep()` and `oxphp_async_await()` yield the fiber cooperatively |\n| **Diagnostics** | Production doctor: checks OS limits (ulimit, TCP backlog, epoll/kqueue, container settings), identifies performance bottlenecks (worker queue depth, lock contention, GC/alloc pressure, ZTS stats), and gives specific actionable recommendations |\n\n## Documentation\n\n- [English](docs/en/)\n- [Русский](docs/ru/)\n- [中文](docs/zh/)\n\n## License\n\n[AGPL-3.0](LICENSE)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxphp%2Foxphp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxphp%2Foxphp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxphp%2Foxphp/lists"}