1#![allow(unsafe_code)]
6
7use std::ffi::{CString, c_int};
8
9use ext_php_rs::types::Zval;
10
11unsafe extern "C" {
12 fn folk_zts_thread_init();
13 fn folk_zts_thread_shutdown();
14 fn folk_zts_request_startup() -> c_int;
15 fn folk_zts_request_shutdown();
16 fn folk_zts_execute_script(filename: *const std::ffi::c_char) -> c_int;
17 fn folk_zts_call_dispatch(
18 func_name: *const std::ffi::c_char,
19 method_zval: *mut Zval,
20 params_zval: *mut Zval,
21 retval: *mut Zval,
22 ) -> c_int;
23 fn folk_zts_eval_string(code: *const std::ffi::c_char) -> c_int;
24 fn folk_zts_is_enabled() -> c_int;
25}
26
27pub fn is_zts() -> bool {
29 unsafe { folk_zts_is_enabled() != 0 }
30}
31
32pub struct ZtsThreadGuard {
37 _private: (),
38}
39
40impl ZtsThreadGuard {
41 pub fn new() -> Self {
43 unsafe {
44 folk_zts_thread_init();
45 }
46 Self { _private: () }
47 }
48}
49
50impl Default for ZtsThreadGuard {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl Drop for ZtsThreadGuard {
57 fn drop(&mut self) {
58 unsafe {
59 folk_zts_thread_shutdown();
60 }
61 }
62}
63
64pub fn request_startup() -> anyhow::Result<()> {
69 let ret = unsafe { folk_zts_request_startup() };
70 if ret == 0 {
71 Ok(())
72 } else {
73 anyhow::bail!("php_request_startup failed (code {ret})")
74 }
75}
76
77pub fn request_shutdown() {
79 unsafe {
80 folk_zts_request_shutdown();
81 }
82}
83
84pub fn eval_string(code: &str) -> anyhow::Result<()> {
91 let c_code = CString::new(code).map_err(|_| anyhow::anyhow!("code contains null byte"))?;
92 let ret = unsafe { folk_zts_eval_string(c_code.as_ptr()) };
93 if ret != 0 {
94 Ok(())
95 } else {
96 anyhow::bail!("zend_eval_string failed")
97 }
98}
99
100pub fn execute_script(filename: &str) -> anyhow::Result<()> {
105 let c_filename =
106 CString::new(filename).map_err(|_| anyhow::anyhow!("filename contains null byte"))?;
107 let ret = unsafe { folk_zts_execute_script(c_filename.as_ptr()) };
108 if ret != 0 {
109 Ok(())
110 } else {
111 anyhow::bail!("php_execute_script failed for {filename}")
112 }
113}
114
115pub fn call_dispatch(
123 func_name: &str,
124 method: &str,
125 params: &serde_json::Value,
126) -> anyhow::Result<serde_json::Value> {
127 let c_func =
128 CString::new(func_name).map_err(|_| anyhow::anyhow!("func_name contains null byte"))?;
129
130 let mut method_zval = Zval::new();
132 method_zval
133 .set_string(method, false)
134 .map_err(|e| anyhow::anyhow!("set_string failed: {e}"))?;
135
136 let mut params_zval = crate::zval_convert::value_to_zval(params);
137 let mut retval = Zval::new();
138
139 let ret = unsafe {
140 folk_zts_call_dispatch(
141 c_func.as_ptr(),
142 &raw mut method_zval,
143 &raw mut params_zval,
144 &raw mut retval,
145 )
146 };
147
148 if ret != 0 {
149 anyhow::bail!("call_user_function({func_name}) failed (code {ret})");
150 }
151
152 Ok(crate::zval_convert::zval_to_value(&retval))
153}