Skip to main content

folk_runtime_embed/
ffi.rs

1//! Raw FFI bindings to PHP embed SAPI and our C helper functions.
2//!
3//! We use manual declarations rather than bindgen for the POC — only the
4//! functions we actually call are declared here.
5
6#![allow(non_camel_case_types, clippy::missing_safety_doc, unsafe_code)]
7
8use std::ffi::c_char;
9use std::os::raw::{c_int, c_uint};
10
11/// Opaque PHP zval (Zend value). 16 bytes on 64-bit.
12/// We never access fields directly — only through our C helper functions.
13#[repr(C)]
14pub struct zval {
15    // zval is 16 bytes: value union (8) + type info (8)
16    _data: [u8; 16],
17}
18
19impl zval {
20    /// Create an uninitialized zval (will be set to `IS_UNDEF` by `folk_zval_undef`).
21    pub fn new_undef() -> Self {
22        let mut z = Self { _data: [0; 16] };
23        unsafe { folk_zval_undef(&mut z) };
24        z
25    }
26}
27
28/// Per-request context passed from Rust to our SAPI callbacks.
29/// Must match `folk_request_context_t` in `php_embed_helper.c`.
30#[repr(C)]
31pub struct folk_request_context {
32    pub request_method: *const c_char,
33    pub request_uri: *const c_char,
34    pub query_string: *const c_char,
35    pub content_type: *const c_char,
36    pub content_length: usize,
37    pub path_translated: *const c_char,
38
39    pub post_data: *const c_char,
40    pub post_data_len: usize,
41    pub post_data_read: usize,
42
43    pub cookie_data: *const c_char,
44
45    pub header_names: *const *const c_char,
46    pub header_values: *const *const c_char,
47    pub header_count: usize,
48
49    pub server_name: *const c_char,
50    pub server_port: c_int,
51    pub protocol: *const c_char,
52}
53
54// ── PHP embed SAPI functions (kept for backward compat with POC tests) ──
55
56unsafe extern "C" {
57    pub fn php_embed_init(argc: c_int, argv: *mut *mut c_char) -> c_int;
58    pub fn php_embed_shutdown();
59}
60
61// ── Custom Folk SAPI ─────────────────────────────────────────────
62
63unsafe extern "C" {
64    /// Initialize PHP with our custom Folk SAPI (replaces `php_embed_init`).
65    pub fn folk_sapi_init() -> c_int;
66
67    /// Initialize PHP with custom INI overrides (double-NUL terminated).
68    pub fn folk_sapi_init_with_ini(ini_overrides: *const c_char) -> c_int;
69
70    /// Shutdown our custom SAPI (replaces `php_embed_shutdown`).
71    pub fn folk_sapi_shutdown();
72
73    /// Set the current request context (before `request_startup`).
74    pub fn folk_request_context_set(ctx: *mut folk_request_context);
75
76    /// Clear the current request context (after `request_shutdown`).
77    pub fn folk_request_context_clear();
78}
79
80// ── Response context ─────────────────────────────────────────────
81
82unsafe extern "C" {
83    /// Get the response HTTP status code.
84    pub fn folk_response_status_code() -> c_int;
85
86    /// Get the number of captured response headers.
87    pub fn folk_response_header_count() -> usize;
88
89    /// Get a response header by index. Sets `*out_len`.
90    pub fn folk_response_header_get(index: usize, out_len: *mut usize) -> *const c_char;
91
92    /// Clear response state (between requests).
93    pub fn folk_response_clear();
94
95    /// Free all response resources (thread shutdown).
96    pub fn folk_response_free();
97}
98
99// ── Our C helper functions (from php_embed_helper.c) ─────────────
100
101unsafe extern "C" {
102    pub fn folk_eval_string_safe(code: *const c_char, retval: *mut zval) -> c_int;
103    pub fn folk_execute_script_safe(filename: *const c_char) -> c_int;
104
105    pub fn folk_call_function_safe(
106        func_name: *const c_char,
107        retval: *mut zval,
108        param_count: c_uint,
109        params: *mut zval,
110    ) -> c_int;
111
112    pub fn folk_request_startup_safe() -> c_int;
113    pub fn folk_request_shutdown_safe() -> c_int;
114
115    pub fn folk_get_output(out_len: *mut usize) -> *const c_char;
116    pub fn folk_clear_output();
117    pub fn folk_free_output();
118
119    pub fn folk_zval_get_string(val: *mut zval, out_len: *mut usize) -> *const c_char;
120    pub fn folk_zval_get_long(val: *mut zval) -> i64;
121    pub fn folk_zval_type(val: *mut zval) -> c_int;
122    pub fn folk_zval_dtor(val: *mut zval);
123    pub fn folk_zval_undef(val: *mut zval);
124    pub fn folk_zval_set_string(val: *mut zval, str: *const c_char, len: usize);
125
126    /// Install our output capture handler into the embed SAPI module.
127    /// Only needed for legacy embed SAPI (`boot()`), not for custom SAPI.
128    pub fn folk_install_output_handler();
129}
130
131// ── Signal management ────────────────────────────────────────────
132
133unsafe extern "C" {
134    /// Save current signal handlers (call before `folk_sapi_init`).
135    pub fn folk_signals_save();
136
137    /// Restore saved signal handlers (call after `folk_sapi_init`).
138    pub fn folk_signals_restore();
139
140    /// Install SIGSEGV handler for worker thread protection.
141    pub fn folk_sigsegv_handler_install();
142
143    /// Eval with SIGSEGV protection. Returns -3 on SIGSEGV.
144    pub fn folk_eval_string_protected(code: *const c_char, retval: *mut zval) -> c_int;
145
146    /// Call function with SIGSEGV protection. Returns -3 on SIGSEGV.
147    pub fn folk_call_function_protected(
148        func_name: *const c_char,
149        retval: *mut zval,
150        param_count: c_uint,
151        params: *mut zval,
152    ) -> c_int;
153}
154
155// ── Direct data passing (Phase A+B) ──────────────────────────────
156
157unsafe extern "C" {
158    /// Call PHP function with raw binary args (no base64). Returns -3 on SIGSEGV.
159    pub fn folk_call_with_binary(
160        func_name: *const c_char,
161        method_name: *const c_char,
162        method_name_len: usize,
163        params: *const c_char,
164        params_len: usize,
165        response_buf: *mut *mut c_char,
166        response_len: *mut usize,
167    ) -> c_int;
168
169    /// Free a buffer allocated by `folk_call_with_binary`.
170    pub fn folk_free_buffer(buf: *mut c_char);
171
172    /// Create a new PHP array zval.
173    pub fn folk_array_init(arr: *mut zval);
174
175    /// Add a string to a PHP array by key.
176    pub fn folk_array_add_string(
177        arr: *mut zval,
178        key: *const c_char,
179        key_len: usize,
180        val: *const c_char,
181        val_len: usize,
182    );
183
184    /// Add a long to a PHP array by key.
185    pub fn folk_array_add_long(arr: *mut zval, key: *const c_char, key_len: usize, val: i64);
186
187    /// Add a sub-array to a PHP array by key.
188    pub fn folk_array_add_array(
189        arr: *mut zval,
190        key: *const c_char,
191        key_len: usize,
192        sub_arr: *mut zval,
193    );
194
195    /// Append a string to a PHP array (numeric index).
196    pub fn folk_array_append_string(arr: *mut zval, val: *const c_char, val_len: usize);
197
198    /// Call PHP function with single array argument. Returns -3 on SIGSEGV.
199    pub fn folk_call_with_array(
200        func_name: *const c_char,
201        request_arr: *mut zval,
202        retval: *mut zval,
203    ) -> c_int;
204
205    /// Read string from PHP array by key.
206    pub fn folk_array_get_string(
207        arr: *mut zval,
208        key: *const c_char,
209        key_len: usize,
210        out_len: *mut usize,
211    ) -> *const c_char;
212
213    /// Read long from PHP array by key.
214    pub fn folk_array_get_long(arr: *mut zval, key: *const c_char, key_len: usize) -> i64;
215
216    /// Get array element count.
217    pub fn folk_array_count(arr: *mut zval) -> usize;
218
219    /// Get string from array by numeric index.
220    pub fn folk_array_index_string(
221        arr: *mut zval,
222        index: usize,
223        out_len: *mut usize,
224    ) -> *const c_char;
225}
226
227// ── TSRM thread context (ZTS only) ───────────────────────────────
228
229unsafe extern "C" {
230    /// Allocate TSRM context for current thread (ZTS). Returns NULL on NTS.
231    pub fn folk_thread_init() -> *mut std::ffi::c_void;
232
233    /// Set TSRM context for current thread.
234    pub fn folk_thread_set_ctx(ctx: *mut std::ffi::c_void);
235
236    /// Free TSRM context.
237    pub fn folk_thread_shutdown(ctx: *mut std::ffi::c_void);
238
239    /// Returns 1 if ZTS build, 0 if NTS.
240    pub fn folk_is_zts() -> c_int;
241}
242
243// ── PHP zval type constants ──────────────────────────────────────
244
245pub const IS_UNDEF: c_int = 0;
246pub const IS_NULL: c_int = 1;
247pub const IS_FALSE: c_int = 2;
248pub const IS_TRUE: c_int = 3;
249pub const IS_LONG: c_int = 4;
250pub const IS_DOUBLE: c_int = 5;
251pub const IS_STRING: c_int = 6;
252pub const IS_ARRAY: c_int = 7;
253pub const IS_OBJECT: c_int = 8;