/// Generated ext-php-rs bridge for the `{{ trait_name }}` contract.
///
/// Wraps a PHP callable (stored as an index in a thread-local registry)
/// so it can be used as `Arc<dyn {{ trait_name }}>` from Rust async code.
/// Dispatch blocks on the Tokio runtime (PHP is single-threaded per request).
pub struct {{ bridge_name }} {
handler_index: usize,
}
impl {{ bridge_name }} {
/// Create a bridge from a handler index.
pub fn new(handler_index: usize) -> Self {
Self { handler_index }
}
}
// SAFETY: The bridge holds only a usize (immutable, Copy).
// PHP handler registry lookup is thread-safe via thread-local RefCell.
impl Send for {{ bridge_name }} {}
impl Sync for {{ bridge_name }} {}
impl {{ core_import }}::{{ trait_name }} for {{ bridge_name }} {
fn {{ dispatch_name }}(
&self{{ extra_param }},
{{ wire_name }}: {{ req_path }},
) -> std::pin::Pin<Box<dyn std::future::Future<Output = {{ output_type }}> + Send + '_>> {
Box::pin(async move {
// Invoke the PHP callable synchronously (blocking)
let outcome: {{ wire_output }} = (async {
// Serialize the request to JSON for PHP roundtrip
let req_json = serde_json::to_string(&{{ wire_name }})
.map_err(|e| Box::new(e) as {{ box_err }})?;
let raw_result = std::panic::catch_unwind(AssertUnwindSafe(|| {
PHP_HANDLER_REGISTRY.with(|registry| -> Result<String, String> {
let registry = registry.borrow();
let Some(callable) = registry.get(self.handler_index) else {
return Err(format!("Handler not found at index {}", self.handler_index));
};
// Deserialize JSON request into PHP object
let req_obj = serde_json::from_str::<serde_json::Value>(&req_json)
.map_err(|e| e.to_string())?;
let req_zval = serde_json::json!(req_obj).into();
// Invoke the callable
let resp_zval = callable.try_call(vec![&req_zval])
.map_err(|e| format!("PHP callable invocation failed: {:?}", e))?;
// Serialize response back to JSON
Ok(serde_json::to_string(&resp_zval).unwrap_or_else(|_| "{}".to_string()))
})
}))
.map_err(|_| Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
"PHP handler panicked",
)) as {{ box_err }})?
.map_err(|e| Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
e,
)) as {{ box_err }})?;
// Deserialize the JSON result back into the wire response DTO.
let response: {{ resp_path }} = serde_json::from_str(&raw_result)
.map_err(|e| Box::new(e) as {{ box_err }})?;
Ok(response)
}).await;
{{ tail }}
})
}
}