ext_php_rs/zend/
function.rs1use 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
18pub 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    #[must_use]
35    pub fn end() -> Self {
36        Self {
37            fname: ptr::null::<c_char>(),
38            handler: None,
39            arg_info: ptr::null(),
40            num_args: 0,
41            flags: 0,
42            #[cfg(php84)]
43            doc_comment: ptr::null(),
44            #[cfg(php84)]
45            frameless_function_infos: ptr::null(),
46        }
47    }
48
49    #[must_use]
52    pub fn into_raw(self) -> *mut Self {
53        Box::into_raw(Box::new(self))
54    }
55}
56
57pub type Function = zend_function;
59
60impl Function {
61    #[must_use]
63    pub fn function_type(&self) -> FunctionType {
64        FunctionType::from(unsafe { self.type_ })
65    }
66
67    #[must_use]
69    pub fn try_from_function(name: &str) -> Option<Self> {
70        unsafe {
71            let res = zend_fetch_function_str(name.as_ptr().cast::<c_char>(), name.len());
72            if res.is_null() {
73                return None;
74            }
75            Some(*res)
76        }
77    }
78
79    #[must_use]
81    pub fn try_from_method(class: &str, name: &str) -> Option<Self> {
82        match ClassEntry::try_find(class) {
83            None => None,
84            Some(ce) => unsafe {
85                let res = zend_hash_str_find_ptr_lc(
86                    &raw const ce.function_table,
87                    name.as_ptr().cast::<c_char>(),
88                    name.len(),
89                )
90                .cast::<zend_function>();
91                if res.is_null() {
92                    return None;
93                }
94                Some(*res)
95            },
96        }
97    }
98
99    #[allow(clippy::inline_always)]
129    #[inline(always)]
130    pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
131        let mut retval = Zval::new();
132        let len = params.len();
133        let params = params
134            .into_iter()
135            .map(|val| val.as_zval(false))
136            .collect::<Result<Vec<_>>>()?;
137        let packed = params.into_boxed_slice();
138
139        unsafe {
140            zend_call_known_function(
141                ptr::from_ref(self).cast_mut(),
142                ptr::null_mut(),
143                ptr::null_mut(),
144                &raw mut retval,
145                len.try_into()?,
146                packed.as_ptr().cast_mut(),
147                ptr::null_mut(),
148            );
149        };
150
151        Ok(retval)
152    }
153}