diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-16 14:18:41 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-16 14:35:48 +0900 |
| commit | aa2124fe5d0c96078c034a6e4044b7e81acdf692 (patch) | |
| tree | c9b1597fef555220676f6a5ae554dc81579a4269 /crates/shirabe-php-rpc/src/env.rs | |
| parent | b4ab3df2ec85fbe477d7721344a8cd3630b437a1 (diff) | |
| download | php-shirabe-aa2124fe5d0c96078c034a6e4044b7e81acdf692.tar.gz php-shirabe-aa2124fe5d0c96078c034a6e4044b7e81acdf692.tar.zst php-shirabe-aa2124fe5d0c96078c034a6e4044b7e81acdf692.zip | |
feat(php-rpc): replay Rust-side env writes into the PHP worker
The worker is a long-lived child holding the environment it was handed
at spawn, so `@putenv`, the bin dir the event dispatcher prepends to
PATH, and COMPOSER_DEV_MODE never reached the PHP code running in it.
The shim now journals every write to the three storages PHP exposes, and
the outermost rpc_call replays the entries the worker has not seen yet
through __shirabe_sync_env. Replaying the writes rather than pushing a
whole snapshot keeps the worker's own $_SERVER entries intact.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-rpc/src/env.rs')
| -rw-r--r-- | crates/shirabe-php-rpc/src/env.rs | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/crates/shirabe-php-rpc/src/env.rs b/crates/shirabe-php-rpc/src/env.rs new file mode 100644 index 00000000..1655168f --- /dev/null +++ b/crates/shirabe-php-rpc/src/env.rs @@ -0,0 +1,57 @@ +//! Propagation of environment mutations into the PHP worker. +//! +//! The worker is a long-lived child: it holds the environment it was handed at spawn, so a +//! `putenv()` or a `$_ENV`/`$_SERVER` write the Rust side makes afterwards is invisible to the +//! PHP code running in it — `@putenv` in a `scripts` entry, the bin dir the event dispatcher +//! prepends to `PATH`, `COMPOSER_DEV_MODE`. The shim records every such write; this module +//! replays the ones the worker has not seen yet, before the next call crosses the boundary. +//! +//! TODO(php-runtime): the reverse direction is missing — a write PHP code makes to its own +//! environment is not reflected back into the Rust-side storages. + +use crate::PluginValue; +use shirabe_php_shim::EnvStorageKind; +use std::os::unix::ffi::OsStrExt as _; + +/// How many recorded mutations the worker has already been told about. +static SYNCED: std::sync::Mutex<usize> = std::sync::Mutex::new(0); + +/// The name `__shirabe_sync_env` reads each mutation's target storage under. +fn storage_name(storage: EnvStorageKind) -> &'static str { + match storage { + EnvStorageKind::Process => "process", + EnvStorageKind::Env => "_ENV", + EnvStorageKind::Server => "_SERVER", + } +} + +/// Replays the environment mutations recorded since the last flush into the worker. +/// +/// Called from the outermost `rpc_call` only, so the `__shirabe_sync_env` call it issues re-enters +/// the session instead of recursing into another flush. +pub(crate) fn flush() -> anyhow::Result<()> { + let mut synced = SYNCED.lock().unwrap(); + let (cursor, mutations) = shirabe_php_shim::env_mutations_since(*synced); + if mutations.is_empty() { + return Ok(()); + } + + let mutations = mutations + .into_iter() + .map(|mutation| { + PluginValue::List(vec![ + PluginValue::string(storage_name(mutation.storage)), + PluginValue::String(mutation.key.as_bytes().to_vec()), + match mutation.value { + Some(value) => PluginValue::String(value.as_bytes().to_vec()), + None => PluginValue::Null, + }, + ]) + }) + .collect(); + let outcome = crate::call_function("__shirabe_sync_env", vec![PluginValue::List(mutations)])?; + // The worker only writes the three storages; nothing it does there can throw. + outcome.unwrap_or_else(|throw| panic!("PHP RPC: __shirabe_sync_env threw: {}", throw.message)); + *synced = cursor; + Ok(()) +} |
