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    describe::{abi, Parameter},
8    error::{Error, Result},
9    ffi::{
10        _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY,
11        _zend_expected_type_Z_EXPECTED_BOOL, _zend_expected_type_Z_EXPECTED_DOUBLE,
12        _zend_expected_type_Z_EXPECTED_LONG, _zend_expected_type_Z_EXPECTED_OBJECT,
13        _zend_expected_type_Z_EXPECTED_RESOURCE, _zend_expected_type_Z_EXPECTED_STRING,
14        zend_internal_arg_info, zend_wrong_parameters_count_error,
15    },
16    flags::DataType,
17    types::Zval,
18    zend::ZendType,
19};
20
21/// Represents an argument to a function.
22#[must_use]
23#[derive(Debug)]
24pub struct Arg<'a> {
25    name: String,
26    r#type: DataType,
27    as_ref: bool,
28    allow_null: bool,
29    pub(crate) variadic: bool,
30    default_value: Option<String>,
31    zval: Option<&'a mut Zval>,
32    variadic_zvals: Vec<Option<&'a mut Zval>>,
33}
34
35impl<'a> Arg<'a> {
36    /// Creates a new argument.
37    ///
38    /// # Parameters
39    ///
40    /// * `name` - The name of the parameter.
41    /// * `_type` - The type of the parameter.
42    pub fn new<T: Into<String>>(name: T, r#type: DataType) -> Self {
43        Arg {
44            name: name.into(),
45            r#type,
46            as_ref: false,
47            allow_null: false,
48            variadic: false,
49            default_value: None,
50            zval: None,
51            variadic_zvals: vec![],
52        }
53    }
54
55    /// Sets the argument as a reference.
56    #[allow(clippy::wrong_self_convention)]
57    pub fn as_ref(mut self) -> Self {
58        self.as_ref = true;
59        self
60    }
61
62    /// Sets the argument as variadic.
63    pub fn is_variadic(mut self) -> Self {
64        self.variadic = true;
65        self
66    }
67
68    /// Sets the argument as nullable.
69    pub fn allow_null(mut self) -> Self {
70        self.allow_null = true;
71        self
72    }
73
74    /// Sets the default value for the argument.
75    pub fn default<T: Into<String>>(mut self, default: T) -> Self {
76        self.default_value = Some(default.into());
77        self
78    }
79
80    /// Attempts to consume the argument, converting the inner type into `T`.
81    /// Upon success, the result is returned in a [`Result`].
82    ///
83    /// As this function consumes, it cannot return a reference to the
84    /// underlying zval.
85    ///
86    /// # Errors
87    ///
88    /// If the conversion fails (or the argument contains no value), the
89    /// argument is returned in an [`Err`] variant.
90    pub fn consume<T>(mut self) -> Result<T, Self>
91    where
92        for<'b> T: FromZvalMut<'b>,
93    {
94        self.zval
95            .as_mut()
96            .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
97            .ok_or(self)
98    }
99
100    /// Attempts to retrieve the value of the argument.
101    /// This will be None until the [`ArgParser`] is used to parse
102    /// the arguments.
103    pub fn val<T>(&'a mut self) -> Option<T>
104    where
105        T: FromZvalMut<'a>,
106    {
107        self.zval
108            .as_mut()
109            .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
110    }
111
112    /// Retrice all the variadic values for this Rust argument.
113    pub fn variadic_vals<T>(&'a mut self) -> Vec<T>
114    where
115        T: FromZvalMut<'a>,
116    {
117        self.variadic_zvals
118            .iter_mut()
119            .filter_map(|zv| zv.as_mut())
120            .filter_map(|zv| T::from_zval_mut(zv.dereference_mut()))
121            .collect()
122    }
123
124    /// Attempts to return a reference to the arguments internal Zval.
125    ///
126    /// # Returns
127    ///
128    /// * `Some(&Zval)` - The internal zval.
129    /// * `None` - The argument was empty.
130    // TODO: Figure out if we can change this
131    #[allow(clippy::mut_mut)]
132    pub fn zval(&mut self) -> Option<&mut &'a mut Zval> {
133        self.zval.as_mut()
134    }
135
136    /// Attempts to call the argument as a callable with a list of arguments to
137    /// pass to the function. Note that a thrown exception inside the
138    /// callable is not detectable, therefore you should check if the return
139    /// value is valid rather than unwrapping. Returns a result containing the
140    /// return value of the function, or an error.
141    ///
142    /// You should not call this function directly, rather through the
143    /// [`call_user_func`](crate::call_user_func) macro.
144    ///
145    /// # Parameters
146    ///
147    /// * `params` - A list of parameters to call the function with.
148    ///
149    /// # Errors
150    ///
151    /// * `Error::Callable` - The argument is not callable.
152    // TODO: Measure this
153    #[allow(clippy::inline_always)]
154    #[inline(always)]
155    pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
156        self.zval.as_ref().ok_or(Error::Callable)?.try_call(params)
157    }
158
159    /// Returns the internal PHP argument info.
160    pub(crate) fn as_arg_info(&self) -> Result<ArgInfo> {
161        Ok(ArgInfo {
162            name: CString::new(self.name.as_str())?.into_raw(),
163            type_: ZendType::empty_from_type(
164                self.r#type,
165                self.as_ref,
166                self.variadic,
167                self.allow_null,
168            )
169            .ok_or(Error::InvalidCString)?,
170            default_value: match &self.default_value {
171                Some(val) if val.as_str() == "None" => CString::new("null")?.into_raw(),
172                Some(val) => CString::new(val.as_str())?.into_raw(),
173                None => ptr::null(),
174            },
175        })
176    }
177}
178
179impl From<Arg<'_>> for _zend_expected_type {
180    fn from(arg: Arg) -> Self {
181        let type_id = match arg.r#type {
182            DataType::False | DataType::True => _zend_expected_type_Z_EXPECTED_BOOL,
183            DataType::Long => _zend_expected_type_Z_EXPECTED_LONG,
184            DataType::Double => _zend_expected_type_Z_EXPECTED_DOUBLE,
185            DataType::String => _zend_expected_type_Z_EXPECTED_STRING,
186            DataType::Array => _zend_expected_type_Z_EXPECTED_ARRAY,
187            DataType::Object(_) => _zend_expected_type_Z_EXPECTED_OBJECT,
188            DataType::Resource => _zend_expected_type_Z_EXPECTED_RESOURCE,
189            _ => unreachable!(),
190        };
191
192        if arg.allow_null {
193            type_id + 1
194        } else {
195            type_id
196        }
197    }
198}
199
200impl From<Arg<'_>> for Parameter {
201    fn from(val: Arg<'_>) -> Self {
202        Parameter {
203            name: val.name.into(),
204            ty: Some(val.r#type).into(),
205            nullable: val.allow_null,
206            variadic: val.variadic,
207            default: val.default_value.map(abi::RString::from).into(),
208        }
209    }
210}
211
212/// Internal argument information used by Zend.
213pub type ArgInfo = zend_internal_arg_info;
214
215/// Parses the arguments of a function.
216#[must_use]
217pub struct ArgParser<'a, 'b> {
218    args: Vec<&'b mut Arg<'a>>,
219    min_num_args: Option<usize>,
220    arg_zvals: Vec<Option<&'a mut Zval>>,
221}
222
223impl<'a, 'b> ArgParser<'a, 'b> {
224    /// Builds a new function argument parser.
225    pub fn new(arg_zvals: Vec<Option<&'a mut Zval>>) -> Self {
226        ArgParser {
227            args: vec![],
228            min_num_args: None,
229            arg_zvals,
230        }
231    }
232
233    /// Adds a new argument to the parser.
234    ///
235    /// # Parameters
236    ///
237    /// * `arg` - The argument to add to the parser.
238    pub fn arg(mut self, arg: &'b mut Arg<'a>) -> Self {
239        self.args.push(arg);
240        self
241    }
242
243    /// Sets the next arguments to be added as not required.
244    pub fn not_required(mut self) -> Self {
245        self.min_num_args = Some(self.args.len());
246        self
247    }
248
249    /// Uses the argument parser to parse the arguments contained in the given
250    /// `ExecuteData` object. Returns successfully if the arguments were
251    /// parsed.
252    ///
253    /// This function can only be safely called from within an exported PHP
254    /// function.
255    ///
256    /// # Parameters
257    ///
258    /// * `execute_data` - The execution data from the function.
259    ///
260    /// # Errors
261    ///
262    /// Returns an [`Error`] type if there were too many or too little arguments
263    /// passed to the function. The user has already been notified so you
264    /// should break execution after seeing an error type.
265    ///
266    /// Also returns an error if the number of min/max arguments exceeds
267    /// `u32::MAX`
268    pub fn parse(mut self) -> Result<()> {
269        let max_num_args = self.args.len();
270        let mut min_num_args = self.min_num_args.unwrap_or(max_num_args);
271        let num_args = self.arg_zvals.len();
272        let has_variadic = self.args.last().is_some_and(|arg| arg.variadic);
273        if has_variadic {
274            min_num_args = min_num_args.saturating_sub(1);
275        }
276
277        if num_args < min_num_args || (!has_variadic && num_args > max_num_args) {
278            // SAFETY: Exported C function is safe, return value is unused and parameters
279            // are copied.
280            unsafe {
281                zend_wrong_parameters_count_error(
282                    min_num_args.try_into()?,
283                    max_num_args.try_into()?,
284                );
285            };
286            return Err(Error::IncorrectArguments(num_args, min_num_args));
287        }
288
289        for (i, arg_zval) in self.arg_zvals.into_iter().enumerate() {
290            let arg = match self.args.get_mut(i) {
291                Some(arg) => Some(arg),
292                // Only select the last item if it's variadic
293                None => self.args.last_mut().filter(|arg| arg.variadic),
294            };
295            if let Some(arg) = arg {
296                if arg.variadic {
297                    arg.variadic_zvals.push(arg_zval);
298                } else {
299                    arg.zval = arg_zval;
300                }
301            }
302        }
303
304        Ok(())
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    #![allow(clippy::unwrap_used)]
311    #[cfg(feature = "embed")]
312    use crate::embed::Embed;
313
314    use super::*;
315
316    #[test]
317    fn test_new() {
318        let arg = Arg::new("test", DataType::Long);
319        assert_eq!(arg.name, "test");
320        assert_eq!(arg.r#type, DataType::Long);
321        assert!(!arg.as_ref);
322        assert!(!arg.allow_null);
323        assert!(!arg.variadic);
324        assert!(arg.default_value.is_none());
325        assert!(arg.zval.is_none());
326        assert!(arg.variadic_zvals.is_empty());
327    }
328
329    #[test]
330    fn test_as_ref() {
331        let arg = Arg::new("test", DataType::Long).as_ref();
332        assert!(arg.as_ref);
333    }
334
335    #[test]
336    fn test_is_variadic() {
337        let arg = Arg::new("test", DataType::Long).is_variadic();
338        assert!(arg.variadic);
339    }
340
341    #[test]
342    fn test_allow_null() {
343        let arg = Arg::new("test", DataType::Long).allow_null();
344        assert!(arg.allow_null);
345    }
346
347    #[test]
348    fn test_default() {
349        let arg = Arg::new("test", DataType::Long).default("default");
350        assert_eq!(arg.default_value, Some("default".to_string()));
351
352        // TODO: Validate type
353    }
354
355    #[test]
356    fn test_consume_no_value() {
357        let arg = Arg::new("test", DataType::Long);
358        let result: Result<i32, _> = arg.consume();
359        assert!(result.is_err());
360        assert_eq!(result.unwrap_err().name, "test");
361    }
362
363    #[test]
364    #[cfg(feature = "embed")]
365    fn test_consume() {
366        let mut arg = Arg::new("test", DataType::Long);
367        let mut zval = Zval::from(42);
368        arg.zval = Some(&mut zval);
369
370        let result: Result<i32, _> = arg.consume();
371        assert_eq!(result.unwrap(), 42);
372    }
373
374    #[test]
375    fn test_val_no_value() {
376        let mut arg = Arg::new("test", DataType::Long);
377        let result: Option<i32> = arg.val();
378        assert!(result.is_none());
379    }
380
381    #[test]
382    #[cfg(feature = "embed")]
383    fn test_val() {
384        let mut arg = Arg::new("test", DataType::Long);
385        let mut zval = Zval::from(42);
386        arg.zval = Some(&mut zval);
387
388        let result: Option<i32> = arg.val();
389        assert_eq!(result.unwrap(), 42);
390    }
391
392    #[test]
393    #[cfg(feature = "embed")]
394    fn test_variadic_vals() {
395        let mut arg = Arg::new("test", DataType::Long).is_variadic();
396        let mut zval1 = Zval::from(42);
397        let mut zval2 = Zval::from(43);
398        arg.variadic_zvals.push(Some(&mut zval1));
399        arg.variadic_zvals.push(Some(&mut zval2));
400
401        let result: Vec<i32> = arg.variadic_vals();
402        assert_eq!(result, vec![42, 43]);
403    }
404
405    #[test]
406    fn test_zval_no_value() {
407        let mut arg = Arg::new("test", DataType::Long);
408        let result = arg.zval();
409        assert!(result.is_none());
410    }
411
412    #[test]
413    #[cfg(feature = "embed")]
414    fn test_zval() {
415        let mut arg = Arg::new("test", DataType::Long);
416        let mut zval = Zval::from(42);
417        arg.zval = Some(&mut zval);
418
419        let result = arg.zval();
420        assert!(result.is_some());
421        assert_eq!(result.unwrap().dereference_mut().long(), Some(42));
422    }
423
424    #[cfg(feature = "embed")]
425    #[test]
426    fn test_try_call_no_value() {
427        let arg = Arg::new("test", DataType::Long);
428        let result = arg.try_call(vec![]);
429        assert!(result.is_err());
430    }
431
432    #[test]
433    #[cfg(feature = "embed")]
434    fn test_try_call_not_callable() {
435        Embed::run(|| {
436            let mut arg = Arg::new("test", DataType::Long);
437            let mut zval = Zval::from(42);
438            arg.zval = Some(&mut zval);
439
440            let result = arg.try_call(vec![]);
441            assert!(result.is_err());
442        });
443    }
444
445    // TODO: Test the callable case
446
447    #[test]
448    #[cfg(feature = "embed")]
449    fn test_as_arg_info() {
450        let arg = Arg::new("test", DataType::Long);
451        let arg_info = arg.as_arg_info();
452        assert!(arg_info.is_ok());
453
454        let arg_info = arg_info.unwrap();
455        assert!(arg_info.default_value.is_null());
456
457        let r#type = arg_info.type_;
458        assert_eq!(r#type.type_mask, 16);
459    }
460
461    #[test]
462    #[cfg(feature = "embed")]
463    fn test_as_arg_info_with_default() {
464        let arg = Arg::new("test", DataType::Long).default("default");
465        let arg_info = arg.as_arg_info();
466        assert!(arg_info.is_ok());
467
468        let arg_info = arg_info.unwrap();
469        assert!(!arg_info.default_value.is_null());
470
471        let r#type = arg_info.type_;
472        assert_eq!(r#type.type_mask, 16);
473    }
474
475    #[test]
476    fn test_type_from_arg() {
477        let arg = Arg::new("test", DataType::Long);
478        let actual: _zend_expected_type = arg.into();
479        assert_eq!(actual, 0);
480
481        let arg = Arg::new("test", DataType::Long).allow_null();
482        let actual: _zend_expected_type = arg.into();
483        assert_eq!(actual, 1);
484
485        let arg = Arg::new("test", DataType::False);
486        let actual: _zend_expected_type = arg.into();
487        assert_eq!(actual, 2);
488
489        let arg = Arg::new("test", DataType::False).allow_null();
490        let actual: _zend_expected_type = arg.into();
491        assert_eq!(actual, 3);
492
493        let arg = Arg::new("test", DataType::True);
494        let actual: _zend_expected_type = arg.into();
495        assert_eq!(actual, 2);
496
497        let arg = Arg::new("test", DataType::True).allow_null();
498        let actual: _zend_expected_type = arg.into();
499        assert_eq!(actual, 3);
500
501        let arg = Arg::new("test", DataType::String);
502        let actual: _zend_expected_type = arg.into();
503        assert_eq!(actual, 4);
504
505        let arg = Arg::new("test", DataType::String).allow_null();
506        let actual: _zend_expected_type = arg.into();
507        assert_eq!(actual, 5);
508
509        let arg = Arg::new("test", DataType::Array);
510        let actual: _zend_expected_type = arg.into();
511        assert_eq!(actual, 6);
512
513        let arg = Arg::new("test", DataType::Array).allow_null();
514        let actual: _zend_expected_type = arg.into();
515        assert_eq!(actual, 7);
516
517        let arg = Arg::new("test", DataType::Resource);
518        let actual: _zend_expected_type = arg.into();
519        assert_eq!(actual, 14);
520
521        let arg = Arg::new("test", DataType::Resource).allow_null();
522        let actual: _zend_expected_type = arg.into();
523        assert_eq!(actual, 15);
524
525        let arg = Arg::new("test", DataType::Object(None));
526        let actual: _zend_expected_type = arg.into();
527        assert_eq!(actual, 18);
528
529        let arg = Arg::new("test", DataType::Object(None)).allow_null();
530        let actual: _zend_expected_type = arg.into();
531        assert_eq!(actual, 19);
532
533        let arg = Arg::new("test", DataType::Double);
534        let actual: _zend_expected_type = arg.into();
535        assert_eq!(actual, 20);
536
537        let arg = Arg::new("test", DataType::Double).allow_null();
538        let actual: _zend_expected_type = arg.into();
539        assert_eq!(actual, 21);
540    }
541
542    #[test]
543    fn test_param_from_arg() {
544        let arg = Arg::new("test", DataType::Long)
545            .default("default")
546            .allow_null();
547        let param: Parameter = arg.into();
548        assert_eq!(param.name, "test".into());
549        assert_eq!(param.ty, abi::Option::Some(DataType::Long));
550        assert!(param.nullable);
551        assert_eq!(param.default, abi::Option::Some("default".into()));
552    }
553
554    #[test]
555    fn test_arg_parser_new() {
556        let arg_zvals = vec![None, None];
557        let parser = ArgParser::new(arg_zvals);
558        assert_eq!(parser.arg_zvals.len(), 2);
559        assert!(parser.args.is_empty());
560        assert!(parser.min_num_args.is_none());
561    }
562
563    #[test]
564    fn test_arg_parser_arg() {
565        let arg_zvals = vec![None, None];
566        let mut parser = ArgParser::new(arg_zvals);
567        let mut arg = Arg::new("test", DataType::Long);
568        parser = parser.arg(&mut arg);
569        assert_eq!(parser.args.len(), 1);
570        assert_eq!(parser.args[0].name, "test");
571        assert_eq!(parser.args[0].r#type, DataType::Long);
572    }
573
574    // TODO: test parse
575}