Skip to main content

folk_runtime_embed/
runtime.rs

1//! `Runtime` implementation for embedded PHP.
2//!
3//! PHP module is initialized once. Worker threads attach to it
4//! and only manage per-request lifecycle.
5
6use anyhow::Result;
7use async_trait::async_trait;
8use folk_core::runtime::{Runtime, WorkerHandle};
9use tracing::info;
10
11use crate::handle::EmbedWorkerHandle;
12use crate::php::PhpInstance;
13use crate::worker::spawn_worker_thread;
14
15/// Configuration for the embed runtime.
16#[derive(Debug, Clone)]
17pub struct EmbedConfig {
18    /// Optional PHP bootstrap script to load on each worker.
19    pub script: Option<String>,
20}
21
22/// Embedded PHP runtime — spawns worker threads instead of processes.
23///
24/// PHP module is initialized once in `new()`. Worker threads call
25/// `PhpInstance::attach()` to share the module without re-init.
26pub struct EmbedRuntime {
27    config: EmbedConfig,
28    /// Keeps the module alive — dropped only when `EmbedRuntime` is dropped.
29    _php_module: PhpInstance,
30}
31
32impl EmbedRuntime {
33    /// Initialize the embed runtime.
34    ///
35    /// Calls `folk_sapi_init()` once — sets up PHP module, extensions,
36    /// `OPcache`, signal handlers. Worker threads will attach to this.
37    ///
38    /// PHP module startup is run in a dedicated OS thread (not a tokio
39    /// worker thread) to give PHP a clean stack and standard thread state.
40    pub fn new(config: EmbedConfig) -> Result<Self> {
41        info!("initializing PHP embed module");
42        let php = PhpInstance::boot_custom_sapi()?;
43        info!("PHP embed module ready");
44
45        Ok(Self {
46            config,
47            _php_module: php,
48        })
49    }
50}
51
52#[async_trait]
53impl Runtime for EmbedRuntime {
54    async fn spawn(&self) -> Result<Box<dyn WorkerHandle>> {
55        let (thread, cmd_tx, task_resp_rx, control_rx, worker_id) =
56            spawn_worker_thread(self.config.script.clone());
57
58        Ok(Box::new(EmbedWorkerHandle::new(
59            worker_id,
60            cmd_tx,
61            task_resp_rx,
62            control_rx,
63            thread,
64        )))
65    }
66}