ext_php_rs/zend/
function.rs

1//! Builder for creating functions and methods in PHP.
2
3use std::{fmt::Debug, os::raw::c_char, ptr};
4
5use crate::{
6    convert::IntoZvalDyn,
7    error::Result,
8    ffi::{
9        zend_call_known_function, zend_fetch_function_str, zend_function, zend_function_entry,
10        zend_hash_str_find_ptr_lc,
11    },
12    flags::FunctionType,
13    types::Zval,
14};
15
16use super::ClassEntry;
17
18/// A Zend function entry.
19pub type FunctionEntry = zend_function_entry;
20
21impl Debug for FunctionEntry {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        f.debug_struct("_zend_function_entry")
24            .field("fname", &self.fname)
25            .field("arg_info", &self.arg_info)
26            .field("num_args", &self.num_args)
27            .field("flags", &self.flags)
28            .finish()
29    }
30}
31
32impl FunctionEntry {
33    /// Returns an empty function entry, signifing the end of a function list.
34    pub fn end() -> Self {
35        Self {
36            fname: ptr::null() as *const c_char,
37            handler: None,
38            arg_info: ptr::null(),
39            num_args: 0,
40            flags: 0,
41            #[cfg(php84)]
42            doc_comment: ptr::null(),
43            #[cfg(php84)]
44            frameless_function_infos: ptr::null(),
45        }
46    }
47
48    /// Converts the function entry into a raw and pointer, releasing it to the
49    /// C world.
50    pub fn into_raw(self) -> *mut Self {
51        Box::into_raw(Box::new(self))
52    }
53}
54
55pub type Function = zend_function;
56
57impl Function {
58    pub fn function_type(&self) -> FunctionType {
59        FunctionType::from(unsafe { self.type_ })
60    }
61
62    pub fn try_from_function(name: &str) -> Option<Self> {
63        unsafe {
64            let res = zend_fetch_function_str(name.as_ptr() as *const c_char, name.len());
65            if res.is_null() {
66                return None;
67            }
68            Some(*res)
69        }
70    }
71    pub fn try_from_method(class: &str, name: &str) -> Option<Self> {
72        match ClassEntry::try_find(class) {
73            None => None,
74            Some(ce) => unsafe {
75                let res = zend_hash_str_find_ptr_lc(
76                    &ce.function_table,
77                    name.as_ptr() as *const c_char,
78                    name.len(),
79                ) as *mut zend_function;
80                if res.is_null() {
81                    return None;
82                }
83                Some(*res)
84            },
85        }
86    }
87
88    /// Attempts to call the callable with a list of arguments to pass to the
89    /// function.
90    ///
91    /// You should not call this function directly, rather through the
92    /// [`call_user_func`] macro.
93    ///
94    /// # Parameters
95    ///
96    /// * `params` - A list of parameters to call the function with.
97    ///
98    /// # Returns
99    ///
100    /// Returns the result wrapped in [`Ok`] upon success. If calling the
101    /// callable fails, or an exception is thrown, an [`Err`] is returned.
102    ///
103    /// # Example
104    ///
105    /// ```no_run
106    /// use ext_php_rs::types::ZendCallable;
107    ///
108    /// let strpos = ZendCallable::try_from_name("strpos").unwrap();
109    /// let result = strpos.try_call(vec![&"hello", &"e"]).unwrap();
110    /// assert_eq!(result.long(), Some(1));
111    /// ```
112    #[inline(always)]
113    pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
114        let mut retval = Zval::new();
115        let len = params.len();
116        let params = params
117            .into_iter()
118            .map(|val| val.as_zval(false))
119            .collect::<Result<Vec<_>>>()?;
120        let packed = params.into_boxed_slice();
121
122        unsafe {
123            zend_call_known_function(
124                self as *const _ as *mut _,
125                std::ptr::null_mut(),
126                std::ptr::null_mut(),
127                &mut retval,
128                len as _,
129                packed.as_ptr() as *mut _,
130                std::ptr::null_mut(),
131            )
132        };
133
134        Ok(retval)
135    }
136}