ext_php_rs/
macros.rs

1//! Macros for interacting with PHP, mainly when the function takes variadic
2//! arguments. Unfortunately, this is the best way to handle these.
3//! Note that most of these will introduce unsafe into your code base.
4
5/// Starts the PHP extension information table displayed when running
6/// `phpinfo();` Must be run *before* rows are inserted into the table.
7#[macro_export]
8macro_rules! info_table_start {
9    () => {
10        unsafe { $crate::ffi::php_info_print_table_start() };
11    };
12}
13
14/// Ends the PHP extension information table. Must be run *after* all rows have
15/// been inserted into the table.
16#[macro_export]
17macro_rules! info_table_end {
18    () => {
19        unsafe { $crate::ffi::php_info_print_table_end() }
20    };
21}
22
23/// Sets the header for the PHP extension information table. Takes as many
24/// string arguments as required.
25#[macro_export]
26macro_rules! info_table_header {
27    ($($element:expr),*) => {$crate::_info_table_row!(php_info_print_table_header, $($element),*)};
28}
29
30/// Adds a row to the PHP extension information table. Takes as many string
31/// arguments as required.
32#[macro_export]
33macro_rules! info_table_row {
34    ($($element:expr),*) => {$crate::_info_table_row!(php_info_print_table_row, $($element),*)};
35}
36
37/// INTERNAL: Calls a variadic C function with the number of parameters, then
38/// following with the parameters.
39#[doc(hidden)]
40#[macro_export]
41macro_rules! _info_table_row {
42    ($fn: ident, $($element: expr),*) => {
43        unsafe {
44            $crate::ffi::$fn($crate::_info_table_row!(@COUNT; $($element),*) as i32, $(::std::ffi::CString::new($element).unwrap().as_ptr()),*);
45        }
46    };
47
48    (@COUNT; $($element: expr),*) => {
49        <[()]>::len(&[$($crate::_info_table_row![@SUBST; $element]),*])
50    };
51    (@SUBST; $_: expr) => { () };
52}
53
54/// Attempts to call a given PHP callable.
55///
56/// # Parameters
57///
58/// * `$fn` - The 'function' to call. Can be an [`Arg`] or a [`Zval`].
59/// * ...`$param` - The parameters to pass to the function. Must be able to be
60///   converted into a [`Zval`].
61///
62/// [`Arg`]: crate::args::Arg
63/// [`Zval`]: crate::types::Zval
64#[macro_export]
65macro_rules! call_user_func {
66    ($fn: expr) => {
67        $fn.try_call(vec![])
68    };
69
70    ($fn: expr, $($param: expr),*) => {
71        $fn.try_call(vec![$(&$param),*])
72    };
73}
74
75/// Parses a given list of arguments using the [`ArgParser`] class.
76///
77/// # Examples
78///
79/// This example parses all of the arguments. If one is invalid, execution of
80/// the function will stop at the `parse_args!` macro invocation. The user is
81/// notified via PHP's argument parsing system.
82///
83/// In this case, all of the arguments are required.
84///
85/// ```
86/// # #[macro_use] extern crate ext_php_rs;
87/// use ext_php_rs::{
88///     parse_args,
89///     args::Arg,
90///     flags::DataType,
91///     zend::ExecuteData,
92///     types::Zval,
93/// };
94///
95/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) {
96///     let mut x = Arg::new("x", DataType::Long);
97///     let mut y = Arg::new("y", DataType::Long);
98///     let mut z = Arg::new("z", DataType::Long);
99///
100///     parse_args!(execute_data, x, y, z);
101/// }
102/// ```
103///
104/// This example is similar to the one above, apart from the fact that the `z`
105/// argument is not required. Note the semicolon separating the first two
106/// arguments from the second.
107///
108/// ```
109/// use ext_php_rs::{
110///     parse_args,
111///     args::Arg,
112///     flags::DataType,
113///     zend::ExecuteData,
114///     types::Zval,
115/// };
116///
117/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) {
118///     let mut x = Arg::new("x", DataType::Long);
119///     let mut y = Arg::new("y", DataType::Long);
120///     let mut z = Arg::new("z", DataType::Long);
121///
122///     parse_args!(execute_data, x, y; z);
123/// }
124/// ```
125///
126/// [`ArgParser`]: crate::args::ArgParser
127#[macro_export]
128macro_rules! parse_args {
129    ($ed: expr, $($arg: expr),*) => {{
130        let parser = $ed.parser()
131            $(.arg(&mut $arg))*
132            .parse();
133        if parser.is_err() {
134            return;
135        }
136    }};
137
138    ($ed: expr, $($arg: expr),* ; $($opt: expr),*) => {{
139        let parser = $ed.parser()
140            $(.arg(&mut $arg))*
141            .not_required()
142            $(.arg(&mut $opt))*
143            .parse();
144        if parser.is_err() {
145            return;
146        }
147    }};
148}
149
150/// Throws an exception and returns from the current function.
151///
152/// Wraps the [`throw`] function by inserting a `return` statement after
153/// throwing the exception.
154///
155/// [`throw`]: crate::exception::throw
156///
157/// # Examples
158///
159/// ```
160/// use ext_php_rs::{
161///     throw,
162///     zend::{ce, ClassEntry, ExecuteData},
163///     types::Zval,
164/// };
165///
166/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) {
167///     let something_wrong = true;
168///     if something_wrong {
169///         throw!(ce::exception(), "Something is wrong!");
170///     }
171///
172///     assert!(false); // This will not run.
173/// }
174/// ```
175#[macro_export]
176macro_rules! throw {
177    ($ex: expr, $reason: expr) => {
178        $crate::exception::throw($ex, $reason);
179        return;
180    };
181}
182
183/// Implements a set of traits required to convert types that implement
184/// [`RegisteredClass`] to and from [`ZendObject`]s and [`Zval`]s. Generally,
185/// this macro should not be called directly, as it is called on any type that
186/// uses the [`php_class`] macro.
187///
188/// The following traits are implemented:
189///
190/// * `FromZendObject for &'a T`
191/// * `FromZendObjectMut for &'a mut T`
192/// * `FromZval for &'a T`
193/// * `FromZvalMut for &'a mut T`
194/// * `IntoZendObject for T`
195/// * `IntoZval for T`
196///
197/// These implementations are required while we wait on the stabilisation of
198/// specialisation.
199///
200/// # Examples
201///
202/// ```
203/// # use ext_php_rs::{convert::{IntoZval, FromZval, IntoZvalDyn}, types::{Zval, ZendObject}, class::{RegisteredClass, ConstructorMeta, ClassEntryInfo}, builders::{ClassBuilder, FunctionBuilder}, zend::ClassEntry, flags::{ClassFlags, MethodFlags}, internal::property::PropertyInfo, describe::DocComments};
204/// use ext_php_rs::class_derives;
205///
206/// struct Test {
207///     a: i32,
208///     b: i64
209/// }
210///
211/// impl RegisteredClass for Test {
212///     const CLASS_NAME: &'static str = "Test";
213///
214///     const BUILDER_MODIFIER: Option<fn(ClassBuilder) -> ClassBuilder> = None;
215///     const EXTENDS: Option<ClassEntryInfo> = None;
216///     const IMPLEMENTS: &'static [ClassEntryInfo] =  &[];
217///     const FLAGS: ClassFlags = ClassFlags::empty();
218///     const DOC_COMMENTS: DocComments = &[];
219///
220///     fn get_metadata() -> &'static ext_php_rs::class::ClassMetadata<Self> {
221///         todo!()
222///     }
223///
224///     fn get_properties<'a>(
225///     ) -> std::collections::HashMap<&'static str, PropertyInfo<'a, Self>>
226///     {
227///         todo!()
228///     }
229///
230///     fn method_builders() -> Vec<(FunctionBuilder<'static>, MethodFlags)> {
231///         todo!()
232///     }
233///
234///     fn constructor() -> Option<ConstructorMeta<Self>> {
235///         todo!()
236///     }
237///
238///     fn constants() -> &'static [(&'static str, &'static dyn IntoZvalDyn, DocComments)] {
239///         todo!()
240///     }
241/// }
242///
243/// class_derives!(Test);
244///
245/// fn into_zval_test() -> Zval {
246///     let x = Test { a: 5, b: 10 };
247///     x.into_zval(false).unwrap()
248/// }
249///
250/// fn from_zval_test<'a>(zv: &'a Zval) -> &'a Test {
251///     <&Test>::from_zval(zv).unwrap()
252/// }
253/// ```
254///
255/// [`RegisteredClass`]: crate::class::RegisteredClass
256/// [`ZendObject`]: crate::types::ZendObject
257/// [`Zval`]: crate::types::Zval
258/// [`php_class`]: crate::php_class
259#[macro_export]
260macro_rules! class_derives {
261    ($type: ty) => {
262        impl<'a> $crate::convert::FromZendObject<'a> for &'a $type {
263            #[inline]
264            fn from_zend_object(obj: &'a $crate::types::ZendObject) -> $crate::error::Result<Self> {
265                let obj = $crate::types::ZendClassObject::<$type>::from_zend_obj(obj)
266                    .ok_or($crate::error::Error::InvalidScope)?;
267                Ok(&**obj)
268            }
269        }
270
271        impl<'a> $crate::convert::FromZendObjectMut<'a> for &'a mut $type {
272            #[inline]
273            fn from_zend_object_mut(
274                obj: &'a mut $crate::types::ZendObject,
275            ) -> $crate::error::Result<Self> {
276                let obj = $crate::types::ZendClassObject::<$type>::from_zend_obj_mut(obj)
277                    .ok_or($crate::error::Error::InvalidScope)?;
278                Ok(&mut **obj)
279            }
280        }
281
282        impl<'a> $crate::convert::FromZval<'a> for &'a $type {
283            const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some(
284                <$type as $crate::class::RegisteredClass>::CLASS_NAME,
285            ));
286
287            #[inline]
288            fn from_zval(zval: &'a $crate::types::Zval) -> ::std::option::Option<Self> {
289                <Self as $crate::convert::FromZendObject>::from_zend_object(zval.object()?).ok()
290            }
291        }
292
293        impl<'a> $crate::convert::FromZvalMut<'a> for &'a mut $type {
294            const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some(
295                <$type as $crate::class::RegisteredClass>::CLASS_NAME,
296            ));
297
298            #[inline]
299            fn from_zval_mut(zval: &'a mut $crate::types::Zval) -> ::std::option::Option<Self> {
300                <Self as $crate::convert::FromZendObjectMut>::from_zend_object_mut(
301                    zval.object_mut()?,
302                )
303                .ok()
304            }
305        }
306
307        impl $crate::convert::IntoZendObject for $type {
308            #[inline]
309            fn into_zend_object(
310                self,
311            ) -> $crate::error::Result<$crate::boxed::ZBox<$crate::types::ZendObject>> {
312                Ok($crate::types::ZendClassObject::new(self).into())
313            }
314        }
315
316        impl $crate::convert::IntoZval for $type {
317            const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some(
318                <$type as $crate::class::RegisteredClass>::CLASS_NAME,
319            ));
320            const NULLABLE: bool = false;
321
322            #[inline]
323            fn set_zval(
324                self,
325                zv: &mut $crate::types::Zval,
326                persistent: bool,
327            ) -> $crate::error::Result<()> {
328                use $crate::convert::IntoZendObject;
329
330                self.into_zend_object()?.set_zval(zv, persistent)
331            }
332        }
333    };
334}
335
336/// Derives `From<T> for Zval` and `IntoZval` for a given type.
337macro_rules! into_zval {
338    ($type: ty, $fn: ident, $dt: ident) => {
339        impl From<$type> for $crate::types::Zval {
340            fn from(val: $type) -> Self {
341                let mut zv = Self::new();
342                zv.$fn(val);
343                zv
344            }
345        }
346
347        impl $crate::convert::IntoZval for $type {
348            const TYPE: $crate::flags::DataType = $crate::flags::DataType::$dt;
349            const NULLABLE: bool = false;
350
351            fn set_zval(self, zv: &mut $crate::types::Zval, _: bool) -> $crate::error::Result<()> {
352                zv.$fn(self);
353                Ok(())
354            }
355        }
356    };
357}
358
359/// Derives `TryFrom<Zval> for T` and `FromZval for T` on a given type.
360macro_rules! try_from_zval {
361    ($type: ty, $fn: ident, $dt: ident) => {
362        impl $crate::convert::FromZval<'_> for $type {
363            const TYPE: $crate::flags::DataType = $crate::flags::DataType::$dt;
364
365            fn from_zval(zval: &$crate::types::Zval) -> ::std::option::Option<Self> {
366                use ::std::convert::TryInto;
367
368                zval.$fn().and_then(|val| val.try_into().ok())
369            }
370        }
371
372        impl ::std::convert::TryFrom<$crate::types::Zval> for $type {
373            type Error = $crate::error::Error;
374
375            fn try_from(value: $crate::types::Zval) -> $crate::error::Result<Self> {
376                <Self as $crate::convert::FromZval>::from_zval(&value)
377                    .ok_or($crate::error::Error::ZvalConversion(value.get_type()))
378            }
379        }
380    };
381}
382
383/// Prints to the PHP standard output, without a newline.
384///
385/// Acts exactly the same as the built-in [`print`] macro.
386///
387/// # Panics
388///
389/// Panics if the generated string could not be converted to a `CString` due to
390/// `NUL` characters.
391#[macro_export]
392macro_rules! php_print {
393    ($arg: tt) => {{
394        $crate::zend::printf($arg).expect("Failed to print to PHP stdout");
395    }};
396
397    ($($arg: tt) *) => {{
398        let args = format!($($arg)*);
399        $crate::zend::printf(args.as_str()).expect("Failed to print to PHP stdout");
400    }};
401}
402
403/// Prints to the PHP standard output, with a newline.
404///
405/// The newline is only a newline character regardless of platform (no carriage
406/// return).
407///
408/// Acts exactly the same as the built-in [`println`] macro.
409///
410/// # Panics
411///
412/// Panics if the generated string could not be converted to a `CString` due to
413/// `NUL` characters.
414#[macro_export]
415macro_rules! php_println {
416    () => {
417        $crate::php_print!("\n");
418    };
419
420    ($fmt: tt) => {
421        $crate::php_print!(concat!($fmt, "\n"));
422    };
423
424    ($fmt: tt, $($arg: tt) *) => {
425        $crate::php_print!(concat!($fmt, "\n"), $($arg)*);
426    };
427}
428
429pub(crate) use into_zval;
430pub(crate) use try_from_zval;