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
104    pub fn folk_call_function_safe(
105        func_name: *const c_char,
106        retval: *mut zval,
107        param_count: c_uint,
108        params: *mut zval,
109    ) -> c_int;
110
111    pub fn folk_request_startup_safe() -> c_int;
112    pub fn folk_request_shutdown_safe() -> c_int;
113
114    pub fn folk_get_output(out_len: *mut usize) -> *const c_char;
115    pub fn folk_clear_output();
116    pub fn folk_free_output();
117
118    pub fn folk_zval_get_string(val: *mut zval, out_len: *mut usize) -> *const c_char;
119    pub fn folk_zval_get_long(val: *mut zval) -> i64;
120    pub fn folk_zval_type(val: *mut zval) -> c_int;
121    pub fn folk_zval_dtor(val: *mut zval);
122    pub fn folk_zval_undef(val: *mut zval);
123    pub fn folk_zval_set_string(val: *mut zval, str: *const c_char, len: usize);
124
125    /// Install our output capture handler into the embed SAPI module.
126    /// Only needed for legacy embed SAPI (`boot()`), not for custom SAPI.
127    pub fn folk_install_output_handler();
128}
129
130// ── Signal management ────────────────────────────────────────────
131
132unsafe extern "C" {
133    /// Save current signal handlers (call before `folk_sapi_init`).
134    pub fn folk_signals_save();
135
136    /// Restore saved signal handlers (call after `folk_sapi_init`).
137    pub fn folk_signals_restore();
138
139    /// Install SIGSEGV handler for worker thread protection.
140    pub fn folk_sigsegv_handler_install();
141
142    /// Eval with SIGSEGV protection. Returns -3 on SIGSEGV.
143    pub fn folk_eval_string_protected(code: *const c_char, retval: *mut zval) -> c_int;
144
145    /// Call function with SIGSEGV protection. Returns -3 on SIGSEGV.
146    pub fn folk_call_function_protected(
147        func_name: *const c_char,
148        retval: *mut zval,
149        param_count: c_uint,
150        params: *mut zval,
151    ) -> c_int;
152}
153
154// ── Direct data passing (Phase A+B) ──────────────────────────────
155
156unsafe extern "C" {
157    /// Call PHP function with raw binary args (no base64). Returns -3 on SIGSEGV.
158    pub fn folk_call_with_binary(
159        func_name: *const c_char,
160        method_name: *const c_char,
161        method_name_len: usize,
162        params: *const c_char,
163        params_len: usize,
164        response_buf: *mut *mut c_char,
165        response_len: *mut usize,
166    ) -> c_int;
167
168    /// Free a buffer allocated by `folk_call_with_binary`.
169    pub fn folk_free_buffer(buf: *mut c_char);
170
171    /// Create a new PHP array zval.
172    pub fn folk_array_init(arr: *mut zval);
173
174    /// Add a string to a PHP array by key.
175    pub fn folk_array_add_string(
176        arr: *mut zval,
177        key: *const c_char,
178        key_len: usize,
179        val: *const c_char,
180        val_len: usize,
181    );
182
183    /// Add a long to a PHP array by key.
184    pub fn folk_array_add_long(arr: *mut zval, key: *const c_char, key_len: usize, val: i64);
185
186    /// Add a sub-array to a PHP array by key.
187    pub fn folk_array_add_array(
188        arr: *mut zval,
189        key: *const c_char,
190        key_len: usize,
191        sub_arr: *mut zval,
192    );
193
194    /// Append a string to a PHP array (numeric index).
195    pub fn folk_array_append_string(arr: *mut zval, val: *const c_char, val_len: usize);
196
197    /// Call PHP function with single array argument. Returns -3 on SIGSEGV.
198    pub fn folk_call_with_array(
199        func_name: *const c_char,
200        request_arr: *mut zval,
201        retval: *mut zval,
202    ) -> c_int;
203
204    /// Read string from PHP array by key.
205    pub fn folk_array_get_string(
206        arr: *mut zval,
207        key: *const c_char,
208        key_len: usize,
209        out_len: *mut usize,
210    ) -> *const c_char;
211
212    /// Read long from PHP array by key.
213    pub fn folk_array_get_long(arr: *mut zval, key: *const c_char, key_len: usize) -> i64;
214
215    /// Get array element count.
216    pub fn folk_array_count(arr: *mut zval) -> usize;
217
218    /// Get string from array by numeric index.
219    pub fn folk_array_index_string(
220        arr: *mut zval,
221        index: usize,
222        out_len: *mut usize,
223    ) -> *const c_char;
224}
225
226// ── PHP zval type constants ──────────────────────────────────────
227
228pub const IS_UNDEF: c_int = 0;
229pub const IS_NULL: c_int = 1;
230pub const IS_FALSE: c_int = 2;
231pub const IS_TRUE: c_int = 3;
232pub const IS_LONG: c_int = 4;
233pub const IS_DOUBLE: c_int = 5;
234pub const IS_STRING: c_int = 6;
235pub const IS_ARRAY: c_int = 7;
236pub const IS_OBJECT: c_int = 8;