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

davidcole1340 / ext-php-rs / 15778794992

20 Jun 2025 12:19PM UTC coverage: 20.64% (-1.4%) from 22.034%
15778794992

Pull #463

github

web-flow
Merge b618ded48 into 660f308c0
Pull Request #463: feat(cargo-php): --features, --all-features, --no-default-features

0 of 11 new or added lines in 1 file covered. (0.0%)

52 existing lines in 10 files now uncovered.

761 of 3687 relevant lines covered (20.64%)

3.57 hits per line

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

72.0
/src/builders/function.rs
1
use crate::{
2
    args::{Arg, ArgInfo},
3
    describe::DocComments,
4
    error::{Error, Result},
5
    flags::{DataType, MethodFlags},
6
    types::Zval,
7
    zend::{ExecuteData, FunctionEntry, ZendType},
8
};
9
use std::{ffi::CString, mem, ptr};
10

11
/// Function representation in Rust.
12
#[cfg(not(windows))]
13
pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecuteData, retval: &mut Zval);
14
#[cfg(windows)]
15
pub type FunctionHandler =
16
    extern "vectorcall" fn(execute_data: &mut ExecuteData, retval: &mut Zval);
17

18
/// Function representation in Rust using pointers.
19
#[cfg(not(windows))]
20
type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecuteData, retval: *mut Zval);
21
#[cfg(windows)]
22
type FunctionPointerHandler =
23
    extern "vectorcall" fn(execute_data: *mut ExecuteData, retval: *mut Zval);
24

25
/// Builder for registering a function in PHP.
26
#[must_use]
27
#[derive(Debug)]
28
pub struct FunctionBuilder<'a> {
29
    pub(crate) name: String,
30
    function: FunctionEntry,
31
    pub(crate) args: Vec<Arg<'a>>,
32
    n_req: Option<usize>,
33
    pub(crate) retval: Option<DataType>,
34
    ret_as_ref: bool,
35
    pub(crate) ret_as_null: bool,
36
    pub(crate) docs: DocComments,
37
}
38

39
impl<'a> FunctionBuilder<'a> {
40
    /// Creates a new function builder, used to build functions
41
    /// to be exported to PHP.
42
    ///
43
    /// # Parameters
44
    ///
45
    /// * `name` - The name of the function.
46
    /// * `handler` - The handler to be called when the function is invoked from
47
    ///   PHP.
48
    pub fn new<T: Into<String>>(name: T, handler: FunctionHandler) -> Self {
10✔
49
        Self {
50
            name: name.into(),
30✔
51
            function: FunctionEntry {
20✔
52
                fname: ptr::null(),
53
                // SAFETY: `*mut T` and `&mut T` have the same ABI as long as `*mut T` is non-null,
54
                // aligned and pointing to a `T`. PHP guarantees that these conditions will be met.
55
                handler: Some(unsafe {
56
                    mem::transmute::<FunctionHandler, FunctionPointerHandler>(handler)
57
                }),
58
                arg_info: ptr::null(),
59
                num_args: 0,
60
                flags: 0, // TBD?
61
                #[cfg(php84)]
62
                doc_comment: ptr::null(),
63
                #[cfg(php84)]
64
                frameless_function_infos: ptr::null(),
65
            },
66
            args: vec![],
20✔
67
            n_req: None,
68
            retval: None,
69
            ret_as_ref: false,
70
            ret_as_null: false,
71
            docs: &[],
10✔
72
        }
73
    }
74

75
    /// Create a new function builder for an abstract function that can be used
76
    /// on an abstract class or an interface.
77
    ///
78
    /// # Parameters
79
    ///
80
    /// * `name` - The name of the function.
81
    pub fn new_abstract<T: Into<String>>(name: T) -> Self {
×
82
        Self {
83
            name: name.into(),
×
84
            function: FunctionEntry {
×
85
                fname: ptr::null(),
86
                handler: None,
87
                arg_info: ptr::null(),
88
                num_args: 0,
89
                flags: MethodFlags::Abstract.bits(),
90
                #[cfg(php84)]
91
                doc_comment: ptr::null(),
92
                #[cfg(php84)]
93
                frameless_function_infos: ptr::null(),
94
            },
95
            args: vec![],
×
96
            n_req: None,
97
            retval: None,
98
            ret_as_ref: false,
99
            ret_as_null: false,
100
            docs: &[],
×
101
        }
102
    }
103

104
    /// Creates a constructor builder, used to build the constructor
105
    /// for classes.
106
    ///
107
    /// # Parameters
108
    ///
109
    /// * `handler` - The handler to be called when the function is invoked from
110
    ///   PHP.
111
    pub fn constructor(handler: FunctionHandler) -> Self {
×
112
        Self::new("__construct", handler)
×
113
    }
114

115
    /// Adds an argument to the function.
116
    ///
117
    /// # Parameters
118
    ///
119
    /// * `arg` - The argument to add to the function.
120
    pub fn arg(mut self, arg: Arg<'a>) -> Self {
5✔
121
        self.args.push(arg);
15✔
122
        self
5✔
123
    }
124

125
    /// Sets the rest of the given arguments as not required.
126
    pub fn not_required(mut self) -> Self {
3✔
127
        self.n_req = Some(self.args.len());
3✔
128
        self
3✔
129
    }
130

131
    /// Sets the return value of the function.
132
    ///
133
    /// # Parameters
134
    ///
135
    /// * `type_` - The return type of the function.
136
    /// * `as_ref` - Whether the function returns a reference.
137
    /// * `allow_null` - Whether the function return value is nullable.
138
    pub fn returns(mut self, type_: DataType, as_ref: bool, allow_null: bool) -> Self {
5✔
139
        self.retval = Some(type_);
5✔
140
        self.ret_as_ref = as_ref;
5✔
141
        self.ret_as_null = allow_null;
5✔
142
        self
5✔
143
    }
144

145
    /// Sets the documentation for the function.
146
    /// This is used to generate the PHP stubs for the function.
147
    ///
148
    /// # Parameters
149
    ///
150
    /// * `docs` - The documentation for the function.
151
    pub fn docs(mut self, docs: DocComments) -> Self {
4✔
152
        self.docs = docs;
4✔
153
        self
4✔
154
    }
155

156
    /// Builds the function converting it into a Zend function entry.
157
    ///
158
    /// Returns a result containing the function entry if successful.
159
    ///
160
    /// # Errors
161
    ///
162
    /// * `Error::InvalidCString` - If the function name is not a valid C
163
    ///   string.
164
    /// * `Error::IntegerOverflow` - If the number of arguments is too large.
165
    /// * If arg info for an argument could not be created.
166
    /// * If the function name contains NUL bytes.
167
    pub fn build(mut self) -> Result<FunctionEntry> {
4✔
168
        let mut args = Vec::with_capacity(self.args.len() + 1);
12✔
169
        let mut n_req = self.n_req.unwrap_or(self.args.len());
20✔
170
        let variadic = self.args.last().is_some_and(|arg| arg.variadic);
12✔
171

172
        if variadic {
5✔
173
            self.function.flags |= MethodFlags::Variadic.bits();
2✔
174
            n_req = n_req.saturating_sub(1);
1✔
175
        }
176

177
        // argument header, retval etc
178
        // The first argument is used as `zend_internal_function_info` for the function.
179
        // That struct shares the same memory as `zend_internal_arg_info` which is used
180
        // for the arguments.
181
        args.push(ArgInfo {
8✔
182
            // required_num_args
183
            name: n_req as *const _,
4✔
184
            type_: match self.retval {
4✔
185
                Some(retval) => {
3✔
186
                    ZendType::empty_from_type(retval, self.ret_as_ref, false, self.ret_as_null)
12✔
187
                        .ok_or(Error::InvalidCString)?
6✔
188
                }
189
                None => ZendType::empty(false, false),
1✔
190
            },
UNCOV
191
            default_value: ptr::null(),
×
192
        });
193

194
        // arguments
195
        args.extend(
4✔
UNCOV
196
            self.args
×
UNCOV
197
                .iter()
×
UNCOV
198
                .map(Arg::as_arg_info)
×
UNCOV
199
                .collect::<Result<Vec<_>>>()?,
×
200
        );
201

202
        self.function.fname = CString::new(self.name)?.into_raw();
4✔
UNCOV
203
        self.function.num_args = (args.len() - 1).try_into()?;
×
204
        self.function.arg_info = Box::into_raw(args.into_boxed_slice()) as *const ArgInfo;
4✔
205

UNCOV
206
        Ok(self.function)
×
207
    }
208
}
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

© 2025 Coveralls, Inc