You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Nothing in this port can deliver a per-request/per-worker environment variable
to PHP. Upstream calls this the prepared environment, and it is not a nicety:
it is the only mechanism by which FRANKENPHP_WORKER=1 reaches $_SERVER['FRANKENPHP_WORKER'], which is what vendor/frankenphp/testdata/_executor.php:4 branches on to decide whether to
call frankenphp_handle_request() at all. Without it, 44 upstream fixtures
cannot be run in worker mode, because every one of them silently takes the
regular-mode branch.
Split out of #14, which cannot implement this without touching four files
outside its lane.
Why there is no back door
$_SERVER in this SAPI is built only by frankenphp_register_variables
(vendor/frankenphp/frankenphp.c:1371-1383), which calls go_register_server_variables and then frankenphp_register_variables_from_request_info. The OS environment reaches $_ENV and getenv() (frankenphp.c:1117 swaps php_import_environment_variables
for get_full_env), and never $_SERVER. So exporting FRANKENPHP_WORKER=1
from the process, or putenv()-ing it, does not work. The prepared env is the
mechanism, and there is no substitute.
frankenphp_add_to_prepared_env(name, name_len, val, val_len, size)
(frankenphp.c:1461-1469, declared frankenphp.h:205-206) — populates it.
Upstream calls it from registerPreparedEnv (cgi.go), reached from go_update_request_info (cgi.go:295-297) when len(fc.env) != 0.
frankenphp_merge_with_prepared_env(track_vars_array)
(frankenphp.c:1270-1275, declared frankenphp.h:207) — copies it into $_SERVER. Upstream calls it from go_register_server_variables
(cgi.go:185-187) last, after the known variables and the headers: "The Prepared Environment is registered last and can overwrite any previous
values."
Keys are NUL-terminated in the Go map (worker.go:138 writes "FRANKENPHP_WORKER\x00"), because registerPreparedEnv hands the key
straight through to C. Decide deliberately whether our env map stores the
NUL or appends it at the boundary, and say which in a comment — do not
reproduce the Go representation by accident.
What to build
crates/frankenrust-sys/build.rs — additive only: two .allowlist_function entries, frankenphp_add_to_prepared_env and frankenphp_merge_with_prepared_env, with a comment naming this issue the
way the existing frankenphp_update_local_thread_context entry names server: regular-thread handler, hyper HTTP/1.1 front end, and the async/pthread bridge #13. Hazard:core: worker mode -- the frankenphp_handle_request park/resume loop #14 adds one entry (frankenphp_shutdown_dummy_request) to the
same list. Append yours at the end of the function block rather than
interleaving with the existing comment blocks, so the two diffs do not
collide on the same hunk.
crates/frankenrust-core/src/context.rs — RequestContext gains pub env: Option<...> (upstream fc.env, a PreparedEnv; nil and empty
both mean "do not call into C", per the len(fc.env) != 0 guards at both
call sites — pick one representation and pin it in the doc comment).
Set through RequestContext::new's existing shape or a builder-style
setter; do not break the existing four-argument constructor's call sites in crates/frankenrust-server/src/server.rs and the existing tests.
crates/frankenrust-core/src/callbacks/servervars.rs — go_update_request_info calls frankenphp_add_to_prepared_env once per
entry when env is non-empty, before the rest of its work, mirroring cgi.go:295-297. The two "fc.env (PreparedEnv) is not part of this issue's RequestContext ... there is nothing to check" comments (around :584 and :639) are now false and must be replaced, not left standing.
crates/frankenrust-sys/shim.c's go_register_server_variables — call frankenphp_merge_with_prepared_env(track_vars_array)after the batch
the Rust side supplied, guarded on the batch reporting a non-empty prepared
env. Ordering is the behaviour: last wins.
Acceptance
A unit test in servervars.rs in the shape of the existing slot tests:
install a RequestContext carrying env = {"FRANKENPHP_WORKER": "1"}, call go_update_request_info, and assert the C side saw the add (a test double
over the extern, or the same technique the neighbouring tests already use for
C-facing calls — do not weaken an existing test to make room).
An end-to-end test that actually reads $_SERVER from PHP: a fixture PHP
script that echoes $_SERVER['FRANKENPHP_WORKER'] ?? 'unset', served with
a RequestContext carrying that prepared env, asserting the body is 1; and
the same script with no prepared env asserting unset. This is the criterion
that can actually fail — the unit test alone cannot see the $_SERVER merge.
A test pinning the ordering: a prepared-env key that collides with a
known CGI variable (e.g. SERVER_SOFTWARE) overwrites it, not the reverse
(cgi.go:186).
bash scripts/gate.sh default passes.
Out of scope
getenv()/$_ENV sandboxing (frankenphp.c:661-665's sandboxed_env), go_putenv, and the putenv()-per-worker-script-run release.
WithRequestPreparedEnv as a public request-option API surface — a plain
public field on RequestContext is enough for now.
Gate: default
Agent: opencode
Nothing in this port can deliver a per-request/per-worker environment variable
to PHP. Upstream calls this the prepared environment, and it is not a nicety:
it is the only mechanism by which
FRANKENPHP_WORKER=1reaches$_SERVER['FRANKENPHP_WORKER'], which is whatvendor/frankenphp/testdata/_executor.php:4branches on to decide whether tocall
frankenphp_handle_request()at all. Without it, 44 upstream fixturescannot be run in worker mode, because every one of them silently takes the
regular-mode branch.
Split out of #14, which cannot implement this without touching four files
outside its lane.
Why there is no back door
$_SERVERin this SAPI is built only byfrankenphp_register_variables(
vendor/frankenphp/frankenphp.c:1371-1383), which callsgo_register_server_variablesand thenfrankenphp_register_variables_from_request_info. The OS environment reaches$_ENVandgetenv()(frankenphp.c:1117swapsphp_import_environment_variablesfor
get_full_env), and never$_SERVER. So exportingFRANKENPHP_WORKER=1from the process, or
putenv()-ing it, does not work. The prepared env is themechanism, and there is no substitute.
What exists upstream
frankenphp.c:127—static THREAD_LOCAL HashTable *prepared_env.frankenphp_add_to_prepared_env(name, name_len, val, val_len, size)(
frankenphp.c:1461-1469, declaredfrankenphp.h:205-206) — populates it.Upstream calls it from
registerPreparedEnv(cgi.go), reached fromgo_update_request_info(cgi.go:295-297) whenlen(fc.env) != 0.frankenphp_merge_with_prepared_env(track_vars_array)(
frankenphp.c:1270-1275, declaredfrankenphp.h:207) — copies it into$_SERVER. Upstream calls it fromgo_register_server_variables(
cgi.go:185-187) last, after the known variables and the headers:"The Prepared Environment is registered last and can overwrite any previous
values."
worker.go:138writes"FRANKENPHP_WORKER\x00"), becauseregisterPreparedEnvhands the keystraight through to C. Decide deliberately whether our
envmap stores theNUL or appends it at the boundary, and say which in a comment — do not
reproduce the Go representation by accident.
What to build
crates/frankenrust-sys/build.rs— additive only: two.allowlist_functionentries,frankenphp_add_to_prepared_envandfrankenphp_merge_with_prepared_env, with a comment naming this issue theway the existing
frankenphp_update_local_thread_contextentry names server: regular-thread handler, hyper HTTP/1.1 front end, and the async/pthread bridge #13.Hazard: core: worker mode -- the frankenphp_handle_request park/resume loop #14 adds one entry (
frankenphp_shutdown_dummy_request) to thesame list. Append yours at the end of the function block rather than
interleaving with the existing comment blocks, so the two diffs do not
collide on the same hunk.
crates/frankenrust-core/src/context.rs—RequestContextgainspub env: Option<...>(upstreamfc.env, aPreparedEnv;niland emptyboth mean "do not call into C", per the
len(fc.env) != 0guards at bothcall sites — pick one representation and pin it in the doc comment).
Set through
RequestContext::new's existing shape or a builder-stylesetter; do not break the existing four-argument constructor's call sites in
crates/frankenrust-server/src/server.rsand the existing tests.crates/frankenrust-core/src/callbacks/servervars.rs—go_update_request_infocallsfrankenphp_add_to_prepared_envonce perentry when
envis non-empty, before the rest of its work, mirroringcgi.go:295-297. The two"fc.env (PreparedEnv) is not part of this issue's RequestContext ... there is nothing to check"comments (around:584and:639) are now false and must be replaced, not left standing.crates/frankenrust-sys/shim.c'sgo_register_server_variables— callfrankenphp_merge_with_prepared_env(track_vars_array)after the batchthe Rust side supplied, guarded on the batch reporting a non-empty prepared
env. Ordering is the behaviour: last wins.
Acceptance
servervars.rsin the shape of the existing slot tests:install a
RequestContextcarryingenv = {"FRANKENPHP_WORKER": "1"}, callgo_update_request_info, and assert the C side saw the add (a test doubleover the extern, or the same technique the neighbouring tests already use for
C-facing calls — do not weaken an existing test to make room).
$_SERVERfrom PHP: a fixture PHPscript that
echoes$_SERVER['FRANKENPHP_WORKER'] ?? 'unset', served witha
RequestContextcarrying that prepared env, asserting the body is1; andthe same script with no prepared env asserting
unset. This is the criterionthat can actually fail — the unit test alone cannot see the
$_SERVERmerge.known CGI variable (e.g.
SERVER_SOFTWARE) overwrites it, not the reverse(
cgi.go:186).bash scripts/gate.sh defaultpasses.Out of scope
getenv()/$_ENVsandboxing (frankenphp.c:661-665'ssandboxed_env),go_putenv, and theputenv()-per-worker-script-run release.WithRequestPreparedEnvas a public request-option API surface — a plainpublic field on
RequestContextis enough for now.FRANKENPHP_WORKER=1intoa running server — that belongs to the server-side worker configuration
issue, which consumes this.
Recoveries: 1