ext_php_rs/builders/
function.rs1use crate::{
2 args::{Arg, ArgInfo},
3 describe::DocComments,
4 error::{Error, Result},
5 flags::{DataType, MethodFlags},
6 types::Zval,
7 zend::{ExecuteData, FunctionEntry, ZendType},
8};
9use std::{ffi::CString, mem, ptr};
10
11#[cfg(not(windows))]
13pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecuteData, retval: &mut Zval);
14#[cfg(windows)]
15pub type FunctionHandler =
16 extern "vectorcall" fn(execute_data: &mut ExecuteData, retval: &mut Zval);
17
18#[cfg(not(windows))]
20type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecuteData, retval: *mut Zval);
21#[cfg(windows)]
22type FunctionPointerHandler =
23 extern "vectorcall" fn(execute_data: *mut ExecuteData, retval: *mut Zval);
24
25#[must_use]
27#[derive(Debug)]
28pub struct FunctionBuilder<'a> {
29 pub(crate) name: String,
30 function: FunctionEntry,
31 pub(crate) args: Vec<Arg<'a>>,
32 n_req: Option<usize>,
33 pub(crate) retval: Option<DataType>,
34 ret_as_ref: bool,
35 pub(crate) ret_as_null: bool,
36 pub(crate) docs: DocComments,
37}
38
39impl<'a> FunctionBuilder<'a> {
40 pub fn new<T: Into<String>>(name: T, handler: FunctionHandler) -> Self {
49 Self {
50 name: name.into(),
51 function: FunctionEntry {
52 fname: ptr::null(),
53 handler: Some(unsafe {
56 mem::transmute::<FunctionHandler, FunctionPointerHandler>(handler)
57 }),
58 arg_info: ptr::null(),
59 num_args: 0,
60 flags: 0, #[cfg(php84)]
62 doc_comment: ptr::null(),
63 #[cfg(php84)]
64 frameless_function_infos: ptr::null(),
65 },
66 args: vec![],
67 n_req: None,
68 retval: None,
69 ret_as_ref: false,
70 ret_as_null: false,
71 docs: &[],
72 }
73 }
74
75 pub fn new_abstract<T: Into<String>>(name: T) -> Self {
82 Self {
83 name: name.into(),
84 function: FunctionEntry {
85 fname: ptr::null(),
86 handler: None,
87 arg_info: ptr::null(),
88 num_args: 0,
89 flags: MethodFlags::Abstract.bits(),
90 #[cfg(php84)]
91 doc_comment: ptr::null(),
92 #[cfg(php84)]
93 frameless_function_infos: ptr::null(),
94 },
95 args: vec![],
96 n_req: None,
97 retval: None,
98 ret_as_ref: false,
99 ret_as_null: false,
100 docs: &[],
101 }
102 }
103
104 pub fn constructor(handler: FunctionHandler) -> Self {
112 Self::new("__construct", handler)
113 }
114
115 pub fn arg(mut self, arg: Arg<'a>) -> Self {
121 self.args.push(arg);
122 self
123 }
124
125 pub fn not_required(mut self) -> Self {
127 self.n_req = Some(self.args.len());
128 self
129 }
130
131 pub fn returns(mut self, type_: DataType, as_ref: bool, allow_null: bool) -> Self {
139 self.retval = Some(type_);
140 self.ret_as_ref = as_ref;
141 self.ret_as_null = allow_null && type_ != DataType::Void && type_ != DataType::Mixed;
142 self
143 }
144
145 pub fn docs(mut self, docs: DocComments) -> Self {
152 self.docs = docs;
153 self
154 }
155
156 pub fn build(mut self) -> Result<FunctionEntry> {
168 let mut args = Vec::with_capacity(self.args.len() + 1);
169 let mut n_req = self.n_req.unwrap_or(self.args.len());
170 let variadic = self.args.last().is_some_and(|arg| arg.variadic);
171
172 if variadic {
173 self.function.flags |= MethodFlags::Variadic.bits();
174 n_req = n_req.saturating_sub(1);
175 }
176
177 args.push(ArgInfo {
182 name: n_req as *const _,
184 type_: match self.retval {
185 Some(retval) => {
186 ZendType::empty_from_type(retval, self.ret_as_ref, false, self.ret_as_null)
187 .ok_or(Error::InvalidCString)?
188 }
189 None => ZendType::empty(false, false),
190 },
191 default_value: ptr::null(),
192 });
193
194 args.extend(
196 self.args
197 .iter()
198 .map(Arg::as_arg_info)
199 .collect::<Result<Vec<_>>>()?,
200 );
201
202 self.function.fname = CString::new(self.name)?.into_raw();
203 self.function.num_args = (args.len() - 1).try_into()?;
204 self.function.arg_info = Box::into_raw(args.into_boxed_slice()) as *const ArgInfo;
205
206 Ok(self.function)
207 }
208}