ext_php_rs/
args.rs

1//! Builder and objects relating to function and method arguments.
2
3use std::{ffi::CString, ptr};
4
5use crate::{
6    convert::{FromZvalMut, IntoZvalDyn},
7    error::{Error, Result},
8    ffi::{
9        _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY,
10        _zend_expected_type_Z_EXPECTED_BOOL, _zend_expected_type_Z_EXPECTED_DOUBLE,
11        _zend_expected_type_Z_EXPECTED_LONG, _zend_expected_type_Z_EXPECTED_OBJECT,
12        _zend_expected_type_Z_EXPECTED_RESOURCE, _zend_expected_type_Z_EXPECTED_STRING,
13        zend_internal_arg_info, zend_wrong_parameters_count_error,
14    },
15    flags::DataType,
16    types::Zval,
17    zend::ZendType,
18};
19
20/// Represents an argument to a function.
21#[derive(Debug)]
22pub struct Arg<'a> {
23    name: String,
24    _type: DataType,
25    as_ref: bool,
26    allow_null: bool,
27    variadic: bool,
28    default_value: Option<String>,
29    zval: Option<&'a mut Zval>,
30    variadic_zvals: Vec<Option<&'a mut Zval>>,
31}
32
33impl<'a> Arg<'a> {
34    /// Creates a new argument.
35    ///
36    /// # Parameters
37    ///
38    /// * `name` - The name of the parameter.
39    /// * `_type` - The type of the parameter.
40    pub fn new<T: Into<String>>(name: T, _type: DataType) -> Self {
41        Arg {
42            name: name.into(),
43            _type,
44            as_ref: false,
45            allow_null: false,
46            variadic: false,
47            default_value: None,
48            zval: None,
49            variadic_zvals: vec![],
50        }
51    }
52
53    /// Sets the argument as a reference.
54    #[allow(clippy::wrong_self_convention)]
55    pub fn as_ref(mut self) -> Self {
56        self.as_ref = true;
57        self
58    }
59
60    /// Sets the argument as variadic.
61    pub fn is_variadic(mut self) -> Self {
62        self.variadic = true;
63        self
64    }
65
66    /// Sets the argument as nullable.
67    pub fn allow_null(mut self) -> Self {
68        self.allow_null = true;
69        self
70    }
71
72    /// Sets the default value for the argument.
73    pub fn default<T: Into<String>>(mut self, default: T) -> Self {
74        self.default_value = Some(default.into());
75        self
76    }
77
78    /// Attempts to consume the argument, converting the inner type into `T`.
79    /// Upon success, the result is returned in a [`Result`].
80    ///
81    /// If the conversion fails (or the argument contains no value), the
82    /// argument is returned in an [`Err`] variant.
83    ///
84    /// As this function consumes, it cannot return a reference to the
85    /// underlying zval.
86    pub fn consume<T>(mut self) -> Result<T, Self>
87    where
88        for<'b> T: FromZvalMut<'b>,
89    {
90        self.zval
91            .as_mut()
92            .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
93            .ok_or(self)
94    }
95
96    /// Attempts to retrieve the value of the argument.
97    /// This will be None until the ArgParser is used to parse
98    /// the arguments.
99    pub fn val<T>(&'a mut self) -> Option<T>
100    where
101        T: FromZvalMut<'a>,
102    {
103        self.zval
104            .as_mut()
105            .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
106    }
107
108    /// Retrice all the variadic values for this Rust argument.
109    pub fn variadic_vals<T>(&'a mut self) -> Vec<T>
110    where
111        T: FromZvalMut<'a>,
112    {
113        self.variadic_zvals
114            .iter_mut()
115            .filter_map(|zv| zv.as_mut())
116            .filter_map(|zv| T::from_zval_mut(zv.dereference_mut()))
117            .collect()
118    }
119
120    /// Attempts to return a reference to the arguments internal Zval.
121    ///
122    /// # Returns
123    ///
124    /// * `Some(&Zval)` - The internal zval.
125    /// * `None` - The argument was empty.
126    pub fn zval(&mut self) -> Option<&mut &'a mut Zval> {
127        self.zval.as_mut()
128    }
129
130    /// Attempts to call the argument as a callable with a list of arguments to
131    /// pass to the function. Note that a thrown exception inside the
132    /// callable is not detectable, therefore you should check if the return
133    /// value is valid rather than unwrapping. Returns a result containing the
134    /// return value of the function, or an error.
135    ///
136    /// You should not call this function directly, rather through the
137    /// [`call_user_func`](crate::call_user_func) macro.
138    ///
139    /// # Parameters
140    ///
141    /// * `params` - A list of parameters to call the function with.
142    #[inline(always)]
143    pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
144        self.zval.as_ref().ok_or(Error::Callable)?.try_call(params)
145    }
146
147    /// Returns the internal PHP argument info.
148    pub(crate) fn as_arg_info(&self) -> Result<ArgInfo> {
149        Ok(ArgInfo {
150            name: CString::new(self.name.as_str())?.into_raw(),
151            type_: ZendType::empty_from_type(
152                self._type,
153                self.as_ref,
154                self.variadic,
155                self.allow_null,
156            )
157            .ok_or(Error::InvalidCString)?,
158            default_value: match &self.default_value {
159                Some(val) => CString::new(val.as_str())?.into_raw(),
160                None => ptr::null(),
161            },
162        })
163    }
164}
165
166impl From<Arg<'_>> for _zend_expected_type {
167    fn from(arg: Arg) -> Self {
168        let err = match arg._type {
169            DataType::False | DataType::True => _zend_expected_type_Z_EXPECTED_BOOL,
170            DataType::Long => _zend_expected_type_Z_EXPECTED_LONG,
171            DataType::Double => _zend_expected_type_Z_EXPECTED_DOUBLE,
172            DataType::String => _zend_expected_type_Z_EXPECTED_STRING,
173            DataType::Array => _zend_expected_type_Z_EXPECTED_ARRAY,
174            DataType::Object(_) => _zend_expected_type_Z_EXPECTED_OBJECT,
175            DataType::Resource => _zend_expected_type_Z_EXPECTED_RESOURCE,
176            _ => unreachable!(),
177        };
178
179        if arg.allow_null {
180            err + 1
181        } else {
182            err
183        }
184    }
185}
186
187/// Internal argument information used by Zend.
188pub type ArgInfo = zend_internal_arg_info;
189
190/// Parses the arguments of a function.
191pub struct ArgParser<'a, 'b> {
192    args: Vec<&'b mut Arg<'a>>,
193    min_num_args: Option<usize>,
194    arg_zvals: Vec<Option<&'a mut Zval>>,
195}
196
197impl<'a, 'b> ArgParser<'a, 'b> {
198    /// Builds a new function argument parser.
199    pub fn new(arg_zvals: Vec<Option<&'a mut Zval>>) -> Self {
200        ArgParser {
201            args: vec![],
202            min_num_args: None,
203            arg_zvals,
204        }
205    }
206
207    /// Adds a new argument to the parser.
208    ///
209    /// # Parameters
210    ///
211    /// * `arg` - The argument to add to the parser.
212    pub fn arg(mut self, arg: &'b mut Arg<'a>) -> Self {
213        self.args.push(arg);
214        self
215    }
216
217    /// Sets the next arguments to be added as not required.
218    pub fn not_required(mut self) -> Self {
219        self.min_num_args = Some(self.args.len());
220        self
221    }
222
223    /// Uses the argument parser to parse the arguments contained in the given
224    /// `ExecuteData` object. Returns successfully if the arguments were
225    /// parsed.
226    ///
227    /// This function can only be safely called from within an exported PHP
228    /// function.
229    ///
230    /// # Parameters
231    ///
232    /// * `execute_data` - The execution data from the function.
233    ///
234    /// # Errors
235    ///
236    /// Returns an [`Error`] type if there were too many or too little arguments
237    /// passed to the function. The user has already been notified so you
238    /// should break execution after seeing an error type.
239    pub fn parse(mut self) -> Result<()> {
240        let max_num_args = self.args.len();
241        let min_num_args = self.min_num_args.unwrap_or(max_num_args);
242        let num_args = self.arg_zvals.len();
243        let has_variadic = self.args.last().is_some_and(|arg| arg.variadic);
244
245        if num_args < min_num_args || (!has_variadic && num_args > max_num_args) {
246            // SAFETY: Exported C function is safe, return value is unused and parameters
247            // are copied.
248            unsafe { zend_wrong_parameters_count_error(min_num_args as _, max_num_args as _) };
249            return Err(Error::IncorrectArguments(num_args, min_num_args));
250        }
251
252        for (i, arg_zval) in self.arg_zvals.into_iter().enumerate() {
253            let arg = match self.args.get_mut(i) {
254                Some(arg) => Some(arg),
255                // Only select the last item if it's variadic
256                None => self.args.last_mut().filter(|arg| arg.variadic),
257            };
258            if let Some(arg) = arg {
259                if arg.variadic {
260                    arg.variadic_zvals.push(arg_zval);
261                } else {
262                    arg.zval = arg_zval;
263                }
264            }
265        }
266
267        Ok(())
268    }
269}