Skip to main content

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/// Attempts to call a given PHP callable with named arguments.
76///
77/// This macro supports PHP 8.0+ named arguments, allowing you to pass
78/// arguments by name rather than position.
79///
80/// # Syntax
81///
82/// ```ignore
83/// // Named arguments only
84/// call_user_func_named!(callable, name1: value1, name2: value2)
85///
86/// // Positional arguments followed by named arguments
87/// call_user_func_named!(callable, [pos1, pos2], name1: value1, name2: value2)
88/// ```
89///
90/// # Parameters
91///
92/// * `$fn` - The 'function' to call. Can be an [`Arg`] or a [`Zval`].
93/// * `$name: $value` - Named parameters as `name: value` pairs.
94/// * `[$($pos),*]` - Optional positional parameters in square brackets.
95///
96/// # Examples
97///
98/// ```ignore
99/// use ext_php_rs::{call_user_func_named, types::ZendCallable};
100///
101/// let str_replace = ZendCallable::try_from_name("str_replace").unwrap();
102///
103/// // Using named arguments only
104/// let result = call_user_func_named!(str_replace,
105///     search: "world",
106///     replace: "PHP",
107///     subject: "Hello world"
108/// ).unwrap();
109///
110/// // Mixing positional and named arguments
111/// let result = call_user_func_named!(str_replace, ["world", "PHP"],
112///     subject: "Hello world"
113/// ).unwrap();
114/// ```
115///
116/// [`Arg`]: crate::args::Arg
117/// [`Zval`]: crate::types::Zval
118/// Note: Parameter names must be valid Rust identifiers.
119/// For other names, use `try_call_named` directly.
120#[macro_export]
121macro_rules! call_user_func_named {
122    // Named arguments only
123    ($fn: expr, $($name: ident : $value: expr),+ $(,)?) => {
124        $fn.try_call_named(&[$((stringify!($name), &$value as &dyn $crate::convert::IntoZvalDyn)),+])
125    };
126
127    // Positional arguments followed by named arguments
128    ($fn: expr, [$($pos: expr),* $(,)?], $($name: ident : $value: expr),+ $(,)?) => {
129        $fn.try_call_with_named(
130            &[$(&$pos as &dyn $crate::convert::IntoZvalDyn),*],
131            &[$((stringify!($name), &$value as &dyn $crate::convert::IntoZvalDyn)),+]
132        )
133    };
134
135}
136
137/// Parses a given list of arguments using the [`ArgParser`] class.
138///
139/// # Examples
140///
141/// This example parses all of the arguments. If one is invalid, execution of
142/// the function will stop at the `parse_args!` macro invocation. The user is
143/// notified via PHP's argument parsing system.
144///
145/// In this case, all of the arguments are required.
146///
147/// ```
148/// # #[macro_use] extern crate ext_php_rs;
149/// use ext_php_rs::{
150///     parse_args,
151///     args::Arg,
152///     flags::DataType,
153///     zend::ExecuteData,
154///     types::Zval,
155/// };
156///
157/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) {
158///     let mut x = Arg::new("x", DataType::Long);
159///     let mut y = Arg::new("y", DataType::Long);
160///     let mut z = Arg::new("z", DataType::Long);
161///
162///     parse_args!(execute_data, x, y, z);
163/// }
164/// ```
165///
166/// This example is similar to the one above, apart from the fact that the `z`
167/// argument is not required. Note the semicolon separating the first two
168/// arguments from the second.
169///
170/// ```
171/// use ext_php_rs::{
172///     parse_args,
173///     args::Arg,
174///     flags::DataType,
175///     zend::ExecuteData,
176///     types::Zval,
177/// };
178///
179/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) {
180///     let mut x = Arg::new("x", DataType::Long);
181///     let mut y = Arg::new("y", DataType::Long);
182///     let mut z = Arg::new("z", DataType::Long);
183///
184///     parse_args!(execute_data, x, y; z);
185/// }
186/// ```
187///
188/// [`ArgParser`]: crate::args::ArgParser
189#[macro_export]
190macro_rules! parse_args {
191    ($ed: expr, $($arg: expr),*) => {{
192        let parser = $ed.parser()
193            $(.arg(&mut $arg))*
194            .parse();
195        if parser.is_err() {
196            return;
197        }
198    }};
199
200    ($ed: expr, $($arg: expr),* ; $($opt: expr),*) => {{
201        let parser = $ed.parser()
202            $(.arg(&mut $arg))*
203            .not_required()
204            $(.arg(&mut $opt))*
205            .parse();
206        if parser.is_err() {
207            return;
208        }
209    }};
210}
211
212/// Throws an exception and returns from the current function.
213///
214/// Wraps the [`throw`] function by inserting a `return` statement after
215/// throwing the exception.
216///
217/// [`throw`]: crate::exception::throw
218///
219/// # Examples
220///
221/// ```
222/// use ext_php_rs::{
223///     throw,
224///     zend::{ce, ClassEntry, ExecuteData},
225///     types::Zval,
226/// };
227///
228/// pub extern "C" fn example_fn(execute_data: &mut ExecuteData, _: &mut Zval) {
229///     let something_wrong = true;
230///     if something_wrong {
231///         throw!(ce::exception(), "Something is wrong!");
232///     }
233///
234///     assert!(false); // This will not run.
235/// }
236/// ```
237#[macro_export]
238macro_rules! throw {
239    ($ex: expr, $reason: expr) => {
240        $crate::exception::throw($ex, $reason);
241        return;
242    };
243}
244
245/// Implements a set of traits required to convert types that implement
246/// [`RegisteredClass`] to and from [`ZendObject`]s and [`Zval`]s. Generally,
247/// this macro should not be called directly, as it is called on any type that
248/// uses the [`php_class`] macro.
249///
250/// The following traits are implemented:
251///
252/// * `FromZendObject for &'a T`
253/// * `FromZendObjectMut for &'a mut T`
254/// * `FromZval for &'a T`
255/// * `FromZvalMut for &'a mut T`
256/// * `IntoZendObject for T`
257/// * `IntoZval for T`
258///
259/// These implementations are required while we wait on the stabilisation of
260/// specialisation.
261///
262/// # Examples
263///
264/// ```
265/// # 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};
266/// use ext_php_rs::class_derives;
267///
268/// struct Test {
269///     a: i32,
270///     b: i64
271/// }
272///
273/// impl RegisteredClass for Test {
274///     const CLASS_NAME: &'static str = "Test";
275///
276///     const BUILDER_MODIFIER: Option<fn(ClassBuilder) -> ClassBuilder> = None;
277///     const EXTENDS: Option<ClassEntryInfo> = None;
278///     const IMPLEMENTS: &'static [ClassEntryInfo] =  &[];
279///     const FLAGS: ClassFlags = ClassFlags::empty();
280///     const DOC_COMMENTS: DocComments = &[];
281///
282///     fn get_metadata() -> &'static ext_php_rs::class::ClassMetadata<Self> {
283///         todo!()
284///     }
285///
286///     fn get_properties<'a>(
287///     ) -> std::collections::HashMap<&'static str, PropertyInfo<'a, Self>>
288///     {
289///         todo!()
290///     }
291///
292///     fn method_builders() -> Vec<(FunctionBuilder<'static>, MethodFlags)> {
293///         todo!()
294///     }
295///
296///     fn constructor() -> Option<ConstructorMeta<Self>> {
297///         todo!()
298///     }
299///
300///     fn constants() -> &'static [(&'static str, &'static dyn IntoZvalDyn, DocComments)] {
301///         todo!()
302///     }
303/// }
304///
305/// class_derives!(Test);
306///
307/// fn into_zval_test() -> Zval {
308///     let x = Test { a: 5, b: 10 };
309///     x.into_zval(false).unwrap()
310/// }
311///
312/// fn from_zval_test<'a>(zv: &'a Zval) -> &'a Test {
313///     <&Test>::from_zval(zv).unwrap()
314/// }
315/// ```
316///
317/// [`RegisteredClass`]: crate::class::RegisteredClass
318/// [`ZendObject`]: crate::types::ZendObject
319/// [`Zval`]: crate::types::Zval
320/// [`php_class`]: crate::php_class
321#[macro_export]
322macro_rules! class_derives {
323    ($type: ty) => {
324        impl<'a> $crate::convert::FromZendObject<'a> for &'a $type {
325            #[inline]
326            fn from_zend_object(obj: &'a $crate::types::ZendObject) -> $crate::error::Result<Self> {
327                let obj = $crate::types::ZendClassObject::<$type>::from_zend_obj(obj)
328                    .ok_or($crate::error::Error::InvalidScope)?;
329                Ok(&**obj)
330            }
331        }
332
333        impl<'a> $crate::convert::FromZendObjectMut<'a> for &'a mut $type {
334            #[inline]
335            fn from_zend_object_mut(
336                obj: &'a mut $crate::types::ZendObject,
337            ) -> $crate::error::Result<Self> {
338                let obj = $crate::types::ZendClassObject::<$type>::from_zend_obj_mut(obj)
339                    .ok_or($crate::error::Error::InvalidScope)?;
340                Ok(&mut **obj)
341            }
342        }
343
344        impl<'a> $crate::convert::FromZval<'a> for &'a $type {
345            const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some(
346                <$type as $crate::class::RegisteredClass>::CLASS_NAME,
347            ));
348
349            #[inline]
350            fn from_zval(zval: &'a $crate::types::Zval) -> ::std::option::Option<Self> {
351                <Self as $crate::convert::FromZendObject>::from_zend_object(zval.object()?).ok()
352            }
353        }
354
355        impl<'a> $crate::convert::FromZvalMut<'a> for &'a mut $type {
356            const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some(
357                <$type as $crate::class::RegisteredClass>::CLASS_NAME,
358            ));
359
360            #[inline]
361            fn from_zval_mut(zval: &'a mut $crate::types::Zval) -> ::std::option::Option<Self> {
362                <Self as $crate::convert::FromZendObjectMut>::from_zend_object_mut(
363                    zval.object_mut()?,
364                )
365                .ok()
366            }
367        }
368
369        impl $crate::convert::IntoZendObject for $type {
370            #[inline]
371            fn into_zend_object(
372                self,
373            ) -> $crate::error::Result<$crate::boxed::ZBox<$crate::types::ZendObject>> {
374                Ok($crate::types::ZendClassObject::new(self).into())
375            }
376        }
377
378        impl $crate::convert::IntoZval for $type {
379            const TYPE: $crate::flags::DataType = $crate::flags::DataType::Object(Some(
380                <$type as $crate::class::RegisteredClass>::CLASS_NAME,
381            ));
382            const NULLABLE: bool = false;
383
384            #[inline]
385            fn set_zval(
386                self,
387                zv: &mut $crate::types::Zval,
388                persistent: bool,
389            ) -> $crate::error::Result<()> {
390                use $crate::convert::IntoZendObject;
391
392                self.into_zend_object()?.set_zval(zv, persistent)
393            }
394        }
395    };
396}
397
398/// Derives `From<T> for Zval` and `IntoZval` for a given type.
399macro_rules! into_zval {
400    ($type: ty, $fn: ident, $dt: ident) => {
401        impl From<$type> for $crate::types::Zval {
402            fn from(val: $type) -> Self {
403                let mut zv = Self::new();
404                zv.$fn(val);
405                zv
406            }
407        }
408
409        impl $crate::convert::IntoZval for $type {
410            const TYPE: $crate::flags::DataType = $crate::flags::DataType::$dt;
411            const NULLABLE: bool = false;
412
413            fn set_zval(self, zv: &mut $crate::types::Zval, _: bool) -> $crate::error::Result<()> {
414                zv.$fn(self);
415                Ok(())
416            }
417        }
418    };
419}
420
421/// Derives `TryFrom<Zval> for T` and `FromZval for T` on a given type.
422macro_rules! try_from_zval {
423    ($type: ty, $fn: ident, $dt: ident) => {
424        impl $crate::convert::FromZval<'_> for $type {
425            const TYPE: $crate::flags::DataType = $crate::flags::DataType::$dt;
426
427            fn from_zval(zval: &$crate::types::Zval) -> ::std::option::Option<Self> {
428                use ::std::convert::TryInto;
429
430                zval.$fn().and_then(|val| val.try_into().ok())
431            }
432        }
433
434        impl ::std::convert::TryFrom<$crate::types::Zval> for $type {
435            type Error = $crate::error::Error;
436
437            fn try_from(value: $crate::types::Zval) -> $crate::error::Result<Self> {
438                <Self as $crate::convert::FromZval>::from_zval(&value)
439                    .ok_or($crate::error::Error::ZvalConversion(value.get_type()))
440            }
441        }
442    };
443}
444
445/// Prints to the PHP standard output, without a newline.
446///
447/// Acts exactly the same as the built-in [`print`] macro.
448///
449/// # Panics
450///
451/// Panics if the generated string could not be converted to a `CString` due to
452/// `NUL` characters.
453#[macro_export]
454macro_rules! php_print {
455    ($arg: tt) => {{
456        $crate::zend::printf($arg).expect("Failed to print to PHP stdout");
457    }};
458
459    ($($arg: tt) *) => {{
460        let args = format!($($arg)*);
461        $crate::zend::printf(args.as_str()).expect("Failed to print to PHP stdout");
462    }};
463}
464
465/// Prints to the PHP standard output, with a newline.
466///
467/// The newline is only a newline character regardless of platform (no carriage
468/// return).
469///
470/// Acts exactly the same as the built-in [`println`] macro.
471///
472/// # Panics
473///
474/// Panics if the generated string could not be converted to a `CString` due to
475/// `NUL` characters.
476#[macro_export]
477macro_rules! php_println {
478    () => {
479        $crate::php_print!("\n");
480    };
481
482    ($fmt: tt) => {
483        $crate::php_print!(concat!($fmt, "\n"));
484    };
485
486    ($fmt: tt, $($arg: tt) *) => {
487        $crate::php_print!(concat!($fmt, "\n"), $($arg)*);
488    };
489}
490
491/// Writes binary data to the PHP standard output.
492///
493/// Unlike [`php_print!`], this macro is binary-safe and can handle data
494/// containing `NUL` bytes. It uses the SAPI module's `ub_write` function.
495///
496/// # Arguments
497///
498/// * `$data` - A byte slice (`&[u8]`) or byte literal (`b"..."`) to write.
499///
500/// # Returns
501///
502/// A `Result<usize>` containing the number of bytes written.
503///
504/// # Errors
505///
506/// Returns [`Error::SapiWriteUnavailable`] if the SAPI's `ub_write` function
507/// is not available.
508///
509/// [`Error::SapiWriteUnavailable`]: crate::error::Error::SapiWriteUnavailable
510///
511/// # Examples
512///
513/// ```ignore
514/// use ext_php_rs::php_write;
515///
516/// // Write a byte literal
517/// php_write!(b"Hello World").expect("write failed");
518///
519/// // Write binary data with NUL bytes (would panic with php_print!)
520/// php_write!(b"Hello\x00World").expect("write failed");
521///
522/// // Write a byte slice
523/// let data: &[u8] = &[0x48, 0x65, 0x6c, 0x6c, 0x6f];
524/// php_write!(data).expect("write failed");
525/// ```
526#[macro_export]
527macro_rules! php_write {
528    ($data: expr) => {{ $crate::zend::write($data) }};
529}
530
531/// Writes binary data to PHP's output stream with output buffering support.
532///
533/// This macro is both binary-safe (can handle `NUL` bytes) AND respects PHP's
534/// output buffering (`ob_start()`). Use this when you need both capabilities.
535///
536/// # Arguments
537///
538/// * `$data` - A byte slice (`&[u8]`) or byte literal (`b"..."`) to write.
539///
540/// # Returns
541///
542/// The number of bytes written.
543///
544/// # Comparison
545///
546/// | Macro | Binary-safe | Output Buffering |
547/// |-------|-------------|------------------|
548/// | `php_print!` | No | Yes |
549/// | `php_write!` | Yes | No (unbuffered) |
550/// | `php_output!` | Yes | Yes |
551///
552/// # Examples
553///
554/// ```ignore
555/// use ext_php_rs::php_output;
556///
557/// // Write binary data that will be captured by ob_start()
558/// php_output!(b"Hello\x00World");
559///
560/// // Use with output buffering
561/// // ob_start();
562/// // php_output!(b"captured");
563/// // $data = ob_get_clean(); // Contains "captured"
564/// ```
565#[macro_export]
566macro_rules! php_output {
567    ($data: expr) => {{ $crate::zend::output_write($data) }};
568}