• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 14502041373

16 Apr 2025 08:30PM UTC coverage: 14.129% (+0.7%) from 13.479%
14502041373

push

github

web-flow
style(clippy): apply pedantic rules

Refs: #418

41 of 345 new or added lines in 46 files covered. (11.88%)

48 existing lines in 25 files now uncovered.

553 of 3914 relevant lines covered (14.13%)

1.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.83
/src/args.rs
1
//! Builder and objects relating to function and method arguments.
2

3
use std::{ffi::CString, ptr};
4

5
use 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)]
24
pub 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

35
impl<'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 {
5✔
43
        Arg {
44
            name: name.into(),
5✔
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![],
5✔
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 {
1✔
64
        self.variadic = true;
1✔
65
        self
1✔
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.
UNCOV
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>
2✔
104
    where
105
        T: FromZvalMut<'a>,
106
    {
107
        self.zval
2✔
108
            .as_mut()
109
            .and_then(|zv| T::from_zval_mut(zv.dereference_mut()))
6✔
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> {
3✔
161
        Ok(ArgInfo {
3✔
162
            name: CString::new(self.name.as_str())?.into_raw(),
6✔
163
            type_: ZendType::empty_from_type(
3✔
164
                self.r#type,
3✔
165
                self.as_ref,
3✔
166
                self.variadic,
3✔
167
                self.allow_null,
3✔
168
            )
169
            .ok_or(Error::InvalidCString)?,
3✔
170
            default_value: match &self.default_value {
3✔
171
                Some(val) => CString::new(val.as_str())?.into_raw(),
×
172
                None => ptr::null(),
3✔
173
            },
174
        })
175
    }
176
}
177

178
impl From<Arg<'_>> for _zend_expected_type {
179
    fn from(arg: Arg) -> Self {
×
NEW
180
        let err = match arg.r#type {
×
181
            DataType::False | DataType::True => _zend_expected_type_Z_EXPECTED_BOOL,
×
182
            DataType::Long => _zend_expected_type_Z_EXPECTED_LONG,
×
183
            DataType::Double => _zend_expected_type_Z_EXPECTED_DOUBLE,
×
184
            DataType::String => _zend_expected_type_Z_EXPECTED_STRING,
×
185
            DataType::Array => _zend_expected_type_Z_EXPECTED_ARRAY,
×
186
            DataType::Object(_) => _zend_expected_type_Z_EXPECTED_OBJECT,
×
187
            DataType::Resource => _zend_expected_type_Z_EXPECTED_RESOURCE,
×
188
            _ => unreachable!(),
189
        };
190

191
        if arg.allow_null {
×
192
            err + 1
×
193
        } else {
194
            err
×
195
        }
196
    }
197
}
198

199
impl From<Arg<'_>> for Parameter {
200
    fn from(val: Arg<'_>) -> Self {
×
201
        Parameter {
202
            name: val.name.into(),
×
NEW
203
            ty: Some(val.r#type).into(),
×
204
            nullable: val.allow_null,
×
205
            default: val.default_value.map(abi::RString::from).into(),
×
206
        }
207
    }
208
}
209

210
/// Internal argument information used by Zend.
211
pub type ArgInfo = zend_internal_arg_info;
212

213
/// Parses the arguments of a function.
214
#[must_use]
215
pub struct ArgParser<'a, 'b> {
216
    args: Vec<&'b mut Arg<'a>>,
217
    min_num_args: Option<usize>,
218
    arg_zvals: Vec<Option<&'a mut Zval>>,
219
}
220

221
impl<'a, 'b> ArgParser<'a, 'b> {
222
    /// Builds a new function argument parser.
223
    pub fn new(arg_zvals: Vec<Option<&'a mut Zval>>) -> Self {
2✔
224
        ArgParser {
225
            args: vec![],
2✔
226
            min_num_args: None,
227
            arg_zvals,
228
        }
229
    }
230

231
    /// Adds a new argument to the parser.
232
    ///
233
    /// # Parameters
234
    ///
235
    /// * `arg` - The argument to add to the parser.
236
    pub fn arg(mut self, arg: &'b mut Arg<'a>) -> Self {
2✔
237
        self.args.push(arg);
2✔
238
        self
2✔
239
    }
240

241
    /// Sets the next arguments to be added as not required.
242
    pub fn not_required(mut self) -> Self {
2✔
243
        self.min_num_args = Some(self.args.len());
2✔
244
        self
2✔
245
    }
246

247
    /// Uses the argument parser to parse the arguments contained in the given
248
    /// `ExecuteData` object. Returns successfully if the arguments were
249
    /// parsed.
250
    ///
251
    /// This function can only be safely called from within an exported PHP
252
    /// function.
253
    ///
254
    /// # Parameters
255
    ///
256
    /// * `execute_data` - The execution data from the function.
257
    ///
258
    /// # Errors
259
    ///
260
    /// Returns an [`Error`] type if there were too many or too little arguments
261
    /// passed to the function. The user has already been notified so you
262
    /// should break execution after seeing an error type.
263
    ///
264
    /// Also returns an error if the number of min/max arguments exceeds
265
    /// `u32::MAX`
266
    pub fn parse(mut self) -> Result<()> {
2✔
267
        let max_num_args = self.args.len();
2✔
268
        let mut min_num_args = self.min_num_args.unwrap_or(max_num_args);
2✔
269
        let num_args = self.arg_zvals.len();
2✔
270
        let has_variadic = self.args.last().is_some_and(|arg| arg.variadic);
6✔
271
        if has_variadic {
2✔
272
            min_num_args = min_num_args.saturating_sub(1);
×
273
        }
274

275
        if num_args < min_num_args || (!has_variadic && num_args > max_num_args) {
6✔
276
            // SAFETY: Exported C function is safe, return value is unused and parameters
277
            // are copied.
278
            unsafe {
279
                zend_wrong_parameters_count_error(
NEW
280
                    min_num_args.try_into()?,
×
NEW
281
                    max_num_args.try_into()?,
×
282
                );
283
            };
UNCOV
284
            return Err(Error::IncorrectArguments(num_args, min_num_args));
×
285
        }
286

287
        for (i, arg_zval) in self.arg_zvals.into_iter().enumerate() {
2✔
288
            let arg = match self.args.get_mut(i) {
×
289
                Some(arg) => Some(arg),
2✔
290
                // Only select the last item if it's variadic
291
                None => self.args.last_mut().filter(|arg| arg.variadic),
×
292
            };
293
            if let Some(arg) = arg {
2✔
294
                if arg.variadic {
×
295
                    arg.variadic_zvals.push(arg_zval);
×
296
                } else {
297
                    arg.zval = arg_zval;
2✔
298
                }
299
            }
300
        }
301

302
        Ok(())
×
303
    }
304
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc