From d38cc59893156c3e3fc6a8dfc167af52f942a730 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 19 Aug 2025 12:29:13 +0200 Subject: [PATCH 1/2] document worker mode for extensions --- docs/worker.md | 10 ++++++++++ frankenphp.c | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/worker.md b/docs/worker.md index 81c18266a..7e3cca90b 100644 --- a/docs/worker.md +++ b/docs/worker.md @@ -179,3 +179,13 @@ $handler = static function () use ($workerServer) { // ... ``` + +## Worker mode for extension developers + +### Request lifecycle + +In worker mode FrankenPHP only goes through `RINIT` and `RSHUTDOWN` once per worker thread. In case you need to observe a worker request start and end, you can not rely on `RINIT` and `RSHUTDOWN` phases of the extension lifecycle. Instead, you are able to hook into the `sapi_module.activate` / `sapi_module.deactivate` function pointers to achieve the same effect (you can validate that the current SAPI is FrankenPHP by checking the value of `sapi_module.name`). + +Upon entering the `frankenphp_handle_request()` function, FrankenPHP calls `sapi_deactivate()` in internals which calls the `sapi_module.deactivate` hook if initialized and finaly blocks until a request arrives. Once a request arrives for the worker to handle, FrankenPHP calls `sapi_activate()` in internals which calls the `sapi_module.activate` hook if initialized. + +Be aware that FrankenPHP worker mode injects a dummy request to start the worker even before the first request arrives. diff --git a/frankenphp.c b/frankenphp.c index 102af2db7..db3d8c481 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -122,7 +122,12 @@ static void frankenphp_release_temporary_streams() { ZEND_HASH_FOREACH_END(); } -/* Adapted from php_request_shutdown */ +/* Adapted from php_request_shutdown + * This mimics the RSHUTDOWN phase from PHP for the worker mode. In case you + * need to observe the end of a request being handled, you can hook into the + * `sapi_module.deactivate` function pointer which gets called from + * `sapi_deactivate()`. + * */ static void frankenphp_worker_request_shutdown() { /* Flush all output buffers */ zend_try { php_output_end_all(); } @@ -170,7 +175,12 @@ void frankenphp_add_assoc_str_ex(zval *track_vars_array, char *key, add_assoc_str_ex(track_vars_array, key, keylen, val); } -/* Adapted from php_request_startup() */ +/* Adapted from php_request_startup() + * This mimics the RINIT phase from PHP for the worker mode. In case you need to + * observe a new request being handled, you can hook into the + * `sapi_module.activate` function pointer which gets called from + * `sapi_activate()`. + * */ static int frankenphp_worker_request_startup() { int retval = SUCCESS; From f41c55d12fdafe0a2bd4130706a42c8e9675a05f Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 20 Aug 2025 16:09:44 +0200 Subject: [PATCH 2/2] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kévin Dunglas --- docs/worker.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/worker.md b/docs/worker.md index 7e3cca90b..b87549d4e 100644 --- a/docs/worker.md +++ b/docs/worker.md @@ -180,9 +180,9 @@ $handler = static function () use ($workerServer) { // ... ``` -## Worker mode for extension developers +## Worker Mode for Extension Developers -### Request lifecycle +### Request Lifecycle In worker mode FrankenPHP only goes through `RINIT` and `RSHUTDOWN` once per worker thread. In case you need to observe a worker request start and end, you can not rely on `RINIT` and `RSHUTDOWN` phases of the extension lifecycle. Instead, you are able to hook into the `sapi_module.activate` / `sapi_module.deactivate` function pointers to achieve the same effect (you can validate that the current SAPI is FrankenPHP by checking the value of `sapi_module.name`).