ripht_php_sapi/sapi/
native.rs1use std::marker::PhantomData;
7use std::os::raw::{c_char, c_void};
8use std::ptr::NonNull;
9
10use super::ffi;
11use super::ffi::zend_function_entry;
12use super::ffi::zend_string;
13
14pub type Handler = unsafe extern "C" fn(
15 execute_data: *mut c_void,
16 return_value: *mut ReturnValue,
17);
18
19pub type ReturnValue = ffi::zval;
20
21#[derive(Clone, Copy, Debug)]
22#[repr(transparent)]
23pub struct Function {
24 entry: zend_function_entry,
25}
26
27impl Function {
28 pub const unsafe fn new_unchecked(
36 fname: *const c_char,
37 handler: Handler,
38 arg_info: *const c_void,
39 num_args: u32,
40 ) -> Self {
41 Self {
42 entry: zend_function_entry {
43 fname,
44 handler: Some(handler),
45 arg_info,
46 num_args,
47 flags: 0,
48 frameless_function_infos: std::ptr::null(),
49 doc_comment: std::ptr::null(),
50 },
51 }
52 }
53
54 pub(crate) const fn entry(self) -> zend_function_entry {
55 self.entry
56 }
57}
58
59pub struct Call<'frame> {
60 execute_data: *mut c_void,
61 num_args: u32,
62 _marker: PhantomData<&'frame ReturnValue>,
63}
64
65impl<'frame> Call<'frame> {
66 pub unsafe fn from_execute_data(execute_data: *mut c_void) -> Self {
73 let num_args = unsafe { ffi::call_num_args(execute_data) };
75
76 Self {
77 execute_data,
78 num_args,
79 _marker: PhantomData,
80 }
81 }
82
83 pub fn num_args(&self) -> u32 {
84 self.num_args
85 }
86
87 pub fn arg(&self, n: usize) -> Option<NonNull<ReturnValue>> {
88 if n == 0 || n as u32 > self.num_args {
89 return None;
90 }
91
92 NonNull::new(unsafe { ffi::call_arg_unchecked(self.execute_data, n) })
94 }
95
96 pub fn arg_string(&'frame self, n: usize) -> Option<&'frame [u8]> {
97 let value = self.arg(n)?;
98
99 unsafe { value.as_ref().as_str() }
102 }
103}
104
105pub unsafe fn set_null(return_value: *mut ReturnValue) {
109 if !return_value.is_null() {
110 unsafe { (*return_value).set_null() };
112 }
113}
114
115pub unsafe fn set_string(return_value: *mut ReturnValue, value: &[u8]) {
119 if !return_value.is_null() {
120 unsafe { (*return_value).set_string(zend_string(value)) };
122 }
123}
124
125pub unsafe fn zend_string(value: &[u8]) -> *mut zend_string {
129 unsafe { ffi::zend_string_init_rust(value) }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 unsafe extern "C" fn fake_handler(
138 _execute_data: *mut c_void,
139 _return_value: *mut ReturnValue,
140 ) {
141 }
142
143 #[test]
144 fn function_wraps_static_zend_metadata() {
145 let name = b"ripht_test_native\0";
146
147 let function = unsafe {
150 Function::new_unchecked(
151 name.as_ptr() as *const c_char,
152 fake_handler,
153 std::ptr::null(),
154 2,
155 )
156 };
157
158 let entry = function.entry();
159
160 assert_eq!(entry.fname, name.as_ptr() as *const c_char);
161 assert_eq!(entry.num_args, 2);
162 assert!(entry.handler.is_some());
163 assert!(entry.arg_info.is_null());
164 }
165
166 #[test]
167 fn call_bounds_arguments_against_php_frame_count() {
168 let mut frame = (0..7)
169 .map(|_| ffi::zval {
170 value: ffi::zend_value { lval: 0 },
171 type_info: 0,
172 _u2: 0,
173 })
174 .collect::<Vec<_>>();
175
176 frame[2]._u2 = 2;
177
178 unsafe {
180 frame[5].set_long(10);
181 frame[6].set_long(20);
182 }
183
184 let call = unsafe {
187 Call::from_execute_data(
188 frame
189 .as_mut_ptr()
190 .cast::<c_void>(),
191 )
192 };
193
194 assert_eq!(call.num_args(), 2);
195 assert!(call.arg(0).is_none());
196 assert_eq!(call.arg(1).unwrap().as_ptr(), &mut frame[5] as *mut _);
197 assert_eq!(call.arg(2).unwrap().as_ptr(), &mut frame[6] as *mut _);
198 assert!(call.arg(3).is_none());
199 }
200}