aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-rpc/src/env.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-16 14:18:41 +0900
committernsfisis <nsfisis@gmail.com>2026-08-16 14:35:48 +0900
commitaa2124fe5d0c96078c034a6e4044b7e81acdf692 (patch)
treec9b1597fef555220676f6a5ae554dc81579a4269 /crates/shirabe-php-rpc/src/env.rs
parentb4ab3df2ec85fbe477d7721344a8cd3630b437a1 (diff)
downloadphp-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.rs57
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(())
+}