1use 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#[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 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 #[allow(clippy::wrong_self_convention)]
55 pub fn as_ref(mut self) -> Self {
56 self.as_ref = true;
57 self
58 }
59
60 pub fn is_variadic(mut self) -> Self {
62 self.variadic = true;
63 self
64 }
65
66 pub fn allow_null(mut self) -> Self {
68 self.allow_null = true;
69 self
70 }
71
72 pub fn default<T: Into<String>>(mut self, default: T) -> Self {
74 self.default_value = Some(default.into());
75 self
76 }
77
78 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 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 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 pub fn zval(&mut self) -> Option<&mut &'a mut Zval> {
127 self.zval.as_mut()
128 }
129
130 #[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 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
187pub type ArgInfo = zend_internal_arg_info;
189
190pub 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 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 pub fn arg(mut self, arg: &'b mut Arg<'a>) -> Self {
213 self.args.push(arg);
214 self
215 }
216
217 pub fn not_required(mut self) -> Self {
219 self.min_num_args = Some(self.args.len());
220 self
221 }
222
223 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 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 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}