FrankenPHP and Worker Mode

FrankenPHP 912,500 (https://github.com/php/frankenphp 11,369 ), now under the PHP Foundation, is a Go application server built on Caddy 7,400 with PHP embedded: one binary serves HTTP/1.1, 2 and 3 with automatic HTTPS. Version 1.12.7 (7 August 2026) is current, and its static Linux binary reports FrankenPHP v1.12.7 PHP 8.5.10 Caddy v2.11.4. Classic mode runs each request on a PHP thread. Worker mode boots your script once and feeds it requests in a loop:

public/index.php: a FrankenPHP workerPHP
<?php
[$booted, $served] = [date('H:i:s'), 0];        // runs once per worker: boot, connect, warm up
$handler = function () use (&$served, $booted): void {
  $uri = $_SERVER['REQUEST_URI'];
  echo 'pid ', getmypid(), " booted $booted, request ", ++$served, " $uri\n";
};
$max = (int) ($_SERVER['MAX_REQUESTS'] ?? 0);
for ($n = 0; !$max || $n < $max; $n++) {
  $keepRunning = frankenphp_handle_request($handler);   // superglobals are reset per request
  gc_collect_cycles();
  if (!$keepRunning) break;
}
One worker, restarted after three requestsPHP
MAX_REQUESTS=3 ./frankenphp php-server --root public --listen 127.0.0.1:8423 \
  --worker public/index.php,1 >franken.log 2>&1 & server=$!
sleep 2; for path in /a /b /c /d; do curl -s "127.0.0.1:8423$path"; done; kill $server
Output
pid 1112 booted 09:36:39, request 1 /a
pid 1112 booted 09:36:39, request 2 /b
pid 1112 booted 09:36:39, request 3 /c
pid 1112 booted 09:36:40, request 1 /d

$served survived between requests, so boot costs are paid once and state can leak (Worker Lifecycles). After MAX_REQUESTS a fresh copy booted in the same process: workers are threads.