Architecture Explanation
Nexphant is split into small runtime packages, with nexphant/framework acting as the application-facing meta-package. An application starts from Nexphant\App, but the request path is handled by the server, runtime, core safety primitives, lifecycle ownership, support configuration, and optional feature packages.
flowchart TD
App["Nexphant\\App"]
Framework["nexphant/framework<br/>routes, middleware, attributes, app bootstrap"]
Server["nexphant/server<br/>sockets, HTTP parsing, router dispatch, WebSocket, SSE"]
Runtime["nexphant/runtime<br/>event loop selection, workers, timers, channels, drain"]
Core["nexphant/core<br/>context, cancellation, ownership, resource registry"]
Support["nexphant/support<br/>config, env, cache helpers, extension detection"]
App --> Framework
Framework --> Server
Server --> Runtime
Runtime --> Core
Core --> SupportFeature packages attach to this stack instead of replacing it:
| Package | Role |
|---|---|
nexphant/database | Query builder, drivers, pooling helpers, migrations, cache, and query metrics. |
nexphant/queue | Background jobs, drivers, retries, workers, queue metrics, and coroutine consumers. |
nexphant/scheduler | Timer-based scheduled task runtime. |
nexphant/cache | Array, APCu, and Redis cache stores plus cache warming. |
nexphant/view | Lightweight Blade-like templates and view responses. |
nexphant/observability | Runtime state, metrics, logging, health checks, dashboards, and leak reporting. |
nexphant/console | Runtime, queue, optimize, preload, and diagnostic CLI commands. |
nexphant/dev | File watching and reload tooling for development. |
nexphant/lifecycle | Structured owner scopes and resource cleanup discipline. |
Boot Flow
App::create() stores the initial config, applies support-level runtime flags, resolves the runtime profile, creates the router, and delays expensive server boot work until listen().
flowchart TD
Create["App::create($config)"]
Apply["Support\\RuntimeConfig::apply($config)"]
Resolve["RuntimeConfigResolver::resolve($config)"]
Router["Create Router"]
DB["Apply DB cache and coalesce toggles"]
Listen["listen($port, $host)"]
Env["Load .env and config/app.php"]
Warm["Warm optimized classes and compiled hot paths"]
Merge["Merge normalized server config and runtime profile"]
HttpServer["Create or configure HttpServer"]
Handlers["Register not-found and router dispatch handlers"]
Start{"workers > 1 and supervisor enabled?"}
Single["Start single worker"]
Supervisor["Start supervisor-managed workers"]
Create --> Apply --> Resolve --> Router --> DB --> Listen
Listen --> Env --> Warm --> Merge --> HttpServer --> Handlers --> Start
Start -- "No" --> Single
Start -- "Yes" --> SupervisorThis split keeps configuration cheap and predictable. The application can define routes, middleware, static files, WebSocket handlers, SSE endpoints, telemetry endpoints, and runtime tuning before sockets start accepting traffic.
Runtime Profiles
The runtime resolver converts app config into concrete server settings. runtime_mode chooses a profile:
| Mode | Intent |
|---|---|
auto | Uses native event extensions when available, otherwise balanced defaults. |
low_latency | Lower keep-alive timeout and leaner metrics for native event-loop deployments. |
balanced | General-purpose defaults with metrics and file stats enabled. |
throughput | Higher worker and callback limits for heavier sustained traffic. |
benchmark | Lean profile for repeatable benchmark runs. |
event_loop can be auto, event, ev, uv, or select. socket_driver can be auto, native, or stream. Auto mode prefers native extensions when installed and falls back to portable PHP implementations.
Request Path
An HTTP request moves through the stack in this order:
sequenceDiagram
participant Client
participant Socket as Socket Accept
participant Parser as HttpParser / FastPathEngine
participant ReqRes as ServerRequest + ServerResponse
participant Global as Global Middleware
participant Router as Framework Router
participant Route as Route Middleware
participant Handler as User Handler
participant Writer as Response Writer
Client->>Socket: HTTP request
Socket->>Parser: raw bytes
Parser->>ReqRes: parsed request
ReqRes->>Global: request + response
Global->>Router: dispatch
Router->>Route: matched route
Route->>Handler: invoke
Handler->>Writer: return response data
Writer-->>Client: serialized response
Writer-->>Socket: keep-alive or closeFramework handlers may return arrays, objects, strings, Nexphant\Server\Response, or Nexphant\Server\RawResponse. Arrays and objects are serialized as JSON. Static routes can be promoted into fast JSON, text, or raw responses so the server can bypass the full framework dispatch path for hot endpoints.
Safety Model
Long-running PHP keeps processes alive across requests, so Nexphant separates performance tuning from safety controls.
With runtime_safety enabled, requests use the asynchronous router dispatch path with runtime context, ownership, drain tracking, and lifecycle discipline. Resources can be associated with owners such as workers, requests, fibers, queue jobs, scheduler tasks, connections, and timers.
With performance_mode or runtime_safety: false, Nexphant uses lean defaults and may choose faster synchronous dispatch paths. This is useful for benchmarks or tightly controlled deployments, but applications should only disable safety features when resource lifetimes are already understood.
Worker Lifecycle
Nexphant runs as a persistent server, not as PHP-FPM request-per-process code.
- The main process boots the app and prepares the HTTP server.
- If
workers > 1and supervisor mode is enabled, the supervisor forks workers. - Each worker owns a server loop, accepts sockets, and publishes runtime stats.
- On shutdown, workers stop accepting new sockets and wait for in-flight work.
- Runtime metadata is written to
storage/runtime.pidandstorage/runtime.jsonfor CLI inspection.
Queue workers, scheduled tasks, timers, and background fibers live beside HTTP work in the same runtime model. They share the same need for ownership, cancellation, drain behavior, and metrics.
Observability
Observability is available at three layers:
- Application telemetry via
$app->runtime('/api'), which exposes live, ready, health, and metrics endpoints. - CLI inspection through
nexphant/console, including runtime status, health, metrics, workers, queue stats, retries, and optimize commands. - Package-level metrics from
nexphant/server,nexphant/runtime,nexphant/database,nexphant/queue, andnexphant/observability.
Metrics and tracing can be tuned with metrics, runtime_features, resource_trace, and leak_detection config.
Package Boundaries
The package dependency direction is intentional:
flowchart BT
Support["nexphant/support"]
Core["nexphant/core"]
Lifecycle["nexphant/lifecycle"]
Runtime["nexphant/runtime"]
Server["nexphant/server"]
Framework["nexphant/framework"]
Queue["nexphant/queue"]
Database["nexphant/database"]
Cache["nexphant/cache"]
View["nexphant/view"]
Observability["nexphant/observability"]
Console["nexphant/console"]
Dev["nexphant/dev"]
Core --> Support
Lifecycle --> Core
Runtime --> Core
Runtime --> Lifecycle
Server --> Runtime
Server --> Core
Framework --> Server
Framework --> Runtime
Framework --> Core
Queue --> Runtime
Queue --> Core
Database --> Core
Database --> Support
Cache --> Core
Cache --> Support
View --> Framework
Observability --> Core
Observability --> Support
Console --> Framework
Console --> Observability
Dev --> Frameworksupport provides low-level helpers. core defines runtime safety primitives. runtime orchestrates cooperative execution. server handles protocol and socket work. framework turns those pieces into the developer-facing App. Higher-level packages add capabilities while keeping the runtime-first structure intact.
