How Pext handles HTTP requests: the same model as PHP, with a path out
A reasonable question to ask of any PHP-to-JavaScript transpiler is "what happens to my request lifecycle?" PHP has a very specific runtime model (shared-nothing, request-scoped, bootstrap-on-each-call) and an application written for that model leans on it in ways the code does not always make explicit. The short answer is that Pext keeps that model intact, by default, and only departs from it when you opt in. The longer answer is what this post is for.
The default: same model as PHP
When you run a transpiled Pext application under the default SAPI, a request goes through the same lifecycle it would under PHP. A single process accepts the connection, runs the bootstrap, executes the request handler from top to bottom, sends the response, tears down the request scope, and goes back to waiting. Inside a single request, everything is sequential. Across requests, nothing is shared by default: no globals leak, no $_SESSION state, no autoloaded classes carry over.
This is intentional. The shared-nothing model is the contract a Laravel or Symfony or WordPress codebase has been written against. Reproducing it exactly is the only way that code keeps working without a rewrite. Scaling out is the same operation it is in PHP: run more processes, behind a load balancer, on more machines. Horizontal scaling. No coordination, because there is no shared mutable state in the runtime to coordinate over.
For an honest comparison of where this model came from and what it costs in 2026, see Part 2 of the Leaving PHP series. The short version is that the model is from a different era and is the largest single reason teams hit a performance ceiling. The point of this post is that Pext does not force you to leave that ceiling on day one. It does give you a path past it.
The CLI is a drop-in
The pext command-line interface is on par with the PHP CLI: same flags where they make sense, same conventions for invoking a script by path, same exit code semantics. The Pext CLI accepts a transpiled entry point in the same way php accepts a PHP file, with the script path mapping rule taking .php arguments and resolving them to their .js equivalents on disk. Concretely, that means tools like PHPUnit, Composer, and any custom bin/ script in your project run under pext with the same arguments they ran under php.
Where the CLI is the entry point, you get a drop-in replacement: change the binary, keep everything else. For projects that wire their web server to php-fpm, the equivalent FPM-style SAPI accepts requests over the same FastCGI protocol so Nginx and Apache configurations carry over unchanged. The fastest way to get a transpiled application serving traffic is to leave your infrastructure exactly as it was and swap the executable.
Pext-specific SAPIs
Because Pext is hosted on Node, it can also offer SAPIs that PHP does not. The default CLI and FPM modes give you parity. The additional SAPIs give you optionality.
The first one already in the runtime is an embedded HTTP server, modelled on PHP's built-in -S mode but with the option to keep the process alive across requests instead of tearing the whole interpreter down each time. This is the right SAPI for local development, for short-lived sidecars, and for any workload where the cost of bootstrap dominates and the request handler itself is cheap.
The interesting SAPIs are the ones that depart from the shared-nothing assumption: an async/await SAPI in which one process handles many in-flight requests concurrently, and a multi-threaded SAPI in which a single process uses worker threads to spread CPU-bound work across cores. Neither is the default, and neither is safe to use with code that assumes shared-nothing without first checking that code. The next two sections are about how that check works.
Centralized storage as the unlock
The reason Pext can offer those SAPIs at all is that the runtime separates execution from storage. PHP's globals, static properties, autoload registry, and the engine's internal symbol tables all live in the same process memory as the running code. In Pext, the equivalent state lives in a centralized storage layer that the execution layer talks to through a well-defined interface. Each request gets its own execution context. The storage layer is the only place mutable shared state can live, and the access path to it is the only place isolation rules need to be enforced.
That split is what makes an async/await SAPI viable. Two requests in flight on the same process get two separate execution contexts, each with its own view of the storage layer. There is no globals collision because there are no globals shared across contexts: each context reads and writes through the storage interface, which knows which context is asking. The same property makes a worker-thread SAPI viable: each thread gets its own context and talks to the same storage layer, with the boundary doing the synchronisation work.
From the application's perspective, none of that is visible. The PHP semantics it expects (function-scope variables, request-scoped globals, class statics that reset per request) all behave the same way. The storage layer is what makes it possible to keep those semantics intact while changing the concurrency model underneath.
Async inference at transpile time
The hard part of moving a PHP codebase onto an async SAPI is not the runtime. It is the application. A handler that calls file_get_contents on a URL, runs a slow database query, and then calls a remote API will sit idle for most of its wall-clock time, and idle handlers on an async SAPI hold a slot they could be releasing. Turning those blocking I/O calls into awaited ones is the work that unlocks the SAPI.
Pext takes a transpile-time view of this work. The transpiler can identify which I/O calls in the source have async-capable variants in the runtime (file_get_contents on a URL, curl_exec, the PDO drivers that have non-blocking equivalents, the cache and session backends, the DNS lookups added in the email-validator work), and it can produce a report of which call sites in the application would need to be converted to take advantage of an async SAPI. The report is the input to an assessment: how much of the codebase changes, how much stays the same, what the budget looks like.
Until that work is done, the application runs unchanged on the synchronous SAPI. Until then, the entire PHP infrastructure can be reused as-is for Pext applications: same FPM-style frontends, same load balancers, same horizontal scaling story. The async SAPI is an option, not a requirement.
What you can do today
The CLI and the FPM-style SAPI are the production paths today. The embedded server is in the runtime and used in local development. The async and multi-threaded SAPIs are in active work, with the storage separation that makes them possible the explicit goal of the bootstrap and context-split work currently in flight.
The short summary: if your application is shaped like a PHP application today, Pext will run it like one. If you later want to move to a different concurrency model, the runtime is built to give you that option without forcing it. Same model by default, different model on request. That is the request lifecycle in two sentences.