ext_php_rs/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(clippy::unwrap_used)]
3#![allow(non_upper_case_globals)]
4#![allow(non_camel_case_types)]
5#![allow(non_snake_case)]
6#![cfg_attr(docs, feature(doc_cfg))]
7#![cfg_attr(windows, feature(abi_vectorcall))]
8
9pub mod alloc;
10pub mod args;
11pub mod binary;
12pub mod binary_slice;
13pub mod builders;
14pub mod convert;
15pub mod error;
16pub mod exception;
17pub mod ffi;
18pub mod flags;
19#[macro_use]
20pub mod macros;
21pub mod boxed;
22pub mod class;
23#[cfg(any(docs, feature = "closure"))]
24#[cfg_attr(docs, doc(cfg(feature = "closure")))]
25pub mod closure;
26pub mod constant;
27pub mod describe;
28#[cfg(feature = "embed")]
29pub mod embed;
30#[doc(hidden)]
31pub mod internal;
32pub mod props;
33pub mod rc;
34pub mod types;
35pub mod zend;
36
37/// A module typically glob-imported containing the typically required macros
38/// and imports.
39pub mod prelude {
40
41    pub use crate::builders::ModuleBuilder;
42    #[cfg(any(docs, feature = "closure"))]
43    #[cfg_attr(docs, doc(cfg(feature = "closure")))]
44    pub use crate::closure::Closure;
45    pub use crate::exception::{PhpException, PhpResult};
46    pub use crate::php_class;
47    pub use crate::php_const;
48    pub use crate::php_extern;
49    pub use crate::php_function;
50    pub use crate::php_impl;
51    pub use crate::php_module;
52    pub use crate::php_print;
53    pub use crate::php_println;
54    pub use crate::php_startup;
55    pub use crate::types::ZendCallable;
56    pub use crate::ZvalConvert;
57}
58
59/// `ext-php-rs` version.
60pub const VERSION: &str = env!("CARGO_PKG_VERSION");
61
62/// Whether the extension is compiled for PHP debug mode.
63pub const PHP_DEBUG: bool = cfg!(php_debug);
64
65/// Whether the extension is compiled for PHP thread-safe mode.
66pub const PHP_ZTS: bool = cfg!(php_zts);
67
68/// Attribute used to annotate constants to be exported to PHP.
69///
70/// The declared constant is left intact (apart from the addition of the
71/// `#[allow(dead_code)]` attribute in the case that you do not use the Rust
72/// constant).
73///
74/// These declarations must happen before you declare your [`macro@php_startup`]
75/// function (or [`macro@php_module`] function if you do not have a startup
76/// function).
77///
78/// # Example
79///
80/// ```
81/// # #![cfg_attr(windows, feature(abi_vectorcall))]
82/// # use ext_php_rs::prelude::*;
83/// #[php_const]
84/// const TEST_CONSTANT: i32 = 100;
85///
86/// #[php_const]
87/// const ANOTHER_CONST: &str = "Hello, world!";
88/// # #[php_module]
89/// # pub fn module(module: ModuleBuilder) -> ModuleBuilder {
90/// #     module
91/// # }
92/// ```
93pub use ext_php_rs_derive::php_const;
94
95/// Attribute used to annotate `extern` blocks which are deemed as PHP
96/// functions.
97///
98/// This allows you to 'import' PHP functions into Rust so that they can be
99/// called like regular Rust functions. Parameters can be any type that
100/// implements [`IntoZval`], and the return type can be anything that implements
101/// [`From<Zval>`] (notice how [`Zval`] is consumed rather than borrowed in this
102/// case).
103///
104/// # Panics
105///
106/// The function can panic when called under a few circumstances:
107///
108/// * The function could not be found or was not callable.
109/// * One of the parameters could not be converted into a [`Zval`].
110/// * The actual function call failed internally.
111/// * The output [`Zval`] could not be parsed into the output type.
112///
113/// The last point can be important when interacting with functions that return
114/// unions, such as [`strpos`] which can return an integer or a boolean. In this
115/// case, a [`Zval`] should be returned as parsing a boolean to an integer is
116/// invalid, and vice versa.
117///
118/// # Example
119///
120/// This `extern` block imports the [`strpos`] function from PHP. Notice that
121/// the string parameters can take either [`String`] or [`&str`], the optional
122/// parameter `offset` is an [`Option<i64>`], and the return value is a [`Zval`]
123/// as the return type is an integer-boolean union.
124///
125/// ```
126/// # #![cfg_attr(windows, feature(abi_vectorcall))]
127/// # use ext_php_rs::prelude::*;
128/// # use ext_php_rs::types::Zval;
129/// #[php_extern]
130/// extern "C" {
131///     fn strpos(haystack: &str, needle: &str, offset: Option<i64>) -> Zval;
132/// }
133///
134/// #[php_function]
135/// pub fn my_strpos() {
136///     assert_eq!(unsafe { strpos("Hello", "e", None) }.long(), Some(1));
137/// }
138/// # #[php_module]
139/// # pub fn module(module: ModuleBuilder) -> ModuleBuilder {
140/// #     module
141/// # }
142/// ```
143///
144/// [`strpos`]: https://www.php.net/manual/en/function.strpos.php
145/// [`IntoZval`]: crate::convert::IntoZval
146/// [`Zval`]: crate::types::Zval
147pub use ext_php_rs_derive::php_extern;
148
149/// Attribute used to annotate a function as a PHP function.
150///
151/// Only types that implement [`FromZval`] can be used as parameter and return
152/// types. These include but are not limited to the following:
153///
154/// - Most primitive integers ([`i8`], [`i16`], [`i32`], [`i64`], [`u8`],
155///   [`u16`], [`u32`], [`u64`],
156///   [`usize`], [`isize`])
157/// - Double-precision floating point numbers ([`f64`])
158/// - [`bool`]
159/// - [`String`]
160/// - [`Vec<T>`] and [`HashMap<String, T>`](std::collections::HashMap) where `T:
161///   FromZval`.
162/// - [`Binary<T>`] for passing binary data as a string, where `T: Pack`.
163/// - [`ZendCallable`] for receiving PHP callables, not applicable for return
164///   values.
165/// - [`Option<T>`] where `T: FromZval`. When used as a parameter, the parameter
166///   will be
167///   deemed nullable, and will contain [`None`] when `null` is passed. When used
168///   as a return type, if [`None`] is returned the [`Zval`] will be set to null.
169///   Optional parameters *must* be of the type [`Option<T>`].
170///
171/// Additionally, you are able to return a variant of [`Result<T, E>`]. `T` must
172/// implement [`IntoZval`] and `E` must implement `Into<PhpException>`. If an
173/// error variant is returned, a PHP exception is thrown using the
174/// [`PhpException`] struct contents.
175///
176/// You are able to implement [`FromZval`] on your own custom types to have
177/// arguments passed in seamlessly. Similarly, you can implement [`IntoZval`] on
178/// values that you want to be able to be returned from PHP functions.
179///
180/// Parameters may be deemed optional by passing the parameter name into the
181/// attribute options. Note that all parameters that are optional (which
182/// includes the given optional parameter as well as all parameters after)
183/// *must* be of the type [`Option<T>`], where `T` is a valid type.
184///
185/// Generics are *not* supported.
186///
187/// Behind the scenes, an `extern "C"` wrapper function is generated, which is
188/// actually called by PHP. The first example function would be converted into a
189/// function which looks like so:
190///
191/// ```no_run
192/// # #![cfg_attr(windows, feature(abi_vectorcall))]
193/// # use ext_php_rs::{prelude::*, exception::PhpException, zend::ExecuteData, convert::{FromZvalMut, IntoZval}, types::Zval, args::{Arg, ArgParser}};
194/// pub fn hello(name: String) -> String {
195///     format!("Hello, {}!", name)
196/// }
197///
198/// pub extern "C" fn _internal_php_hello(ex: &mut ExecuteData, retval: &mut Zval) {
199///     let mut name = Arg::new("name", <String as FromZvalMut>::TYPE);
200///     let parser = ex.parser()
201///         .arg(&mut name)
202///         .parse();
203///
204///     if parser.is_err() {
205///         return;
206///     }
207///
208///     let result = hello(match name.val() {
209///         Some(val) => val,
210///         None => {
211///             PhpException::default("Invalid value given for argument `name`.".into())
212///                 .throw()
213///                 .expect("Failed to throw exception: Invalid value given for argument `name`.");
214///             return;
215///         }
216///     });
217///
218///     match result.set_zval(retval, false) {
219///         Ok(_) => {},
220///         Err(e) => {
221///             let e: PhpException = e.into();
222///             e.throw().expect("Failed to throw exception: Failed to set return value.");
223///         }
224///     };
225/// }
226/// ```
227///
228/// This allows the original function to continue being used while also being
229/// exported as a PHP function.
230///
231/// # Examples
232///
233/// Creating a simple function which will return a string. The function still
234/// must be declared in the PHP module to be able to call.
235///
236/// ```
237/// # #![cfg_attr(windows, feature(abi_vectorcall))]
238/// # use ext_php_rs::prelude::*;
239/// #[php_function]
240/// pub fn hello(name: String) -> String {
241///     format!("Hello, {}!", name)
242/// }
243/// # #[php_module]
244/// # pub fn module(module: ModuleBuilder) -> ModuleBuilder {
245/// #     module
246/// # }
247/// ```
248///
249/// Parameters can also be deemed optional by passing the parameter name in the
250/// attribute options. This function takes one required parameter (`name`) and
251/// two optional parameters (`description` and `age`).
252///
253/// ```
254/// # #![cfg_attr(windows, feature(abi_vectorcall))]
255/// # use ext_php_rs::prelude::*;
256/// #[php_function(optional = "description")]
257/// pub fn hello(name: String, description: Option<String>, age: Option<i32>) -> String {
258///     let mut response = format!("Hello, {}!", name);
259///
260///     if let Some(description) = description {
261///         response.push_str(format!(" {}.", description).as_ref());
262///     }
263///
264///     if let Some(age) = age {
265///         response.push_str(format!(" I am {} year(s) old.", age).as_ref());
266///     }
267///
268///     response
269/// }
270/// # #[php_module]
271/// # pub fn module(module: ModuleBuilder) -> ModuleBuilder {
272/// #     module
273/// # }
274/// ```
275///
276/// Defaults can also be given in a similar fashion. For example, the above
277/// function could have default values for `description` and `age` by changing
278/// the attribute to the following:
279///
280/// ```
281/// # #![cfg_attr(windows, feature(abi_vectorcall))]
282/// # use ext_php_rs::prelude::*;
283/// #[php_function(optional = "description", defaults(description = "David", age = 10))]
284/// pub fn hello(name: String, description: String, age: i32) -> String {
285///     format!("Hello, {}! {}. I am {} year(s) old.", name, description, age)
286/// }
287/// # #[php_module]
288/// # pub fn module(module: ModuleBuilder) -> ModuleBuilder {
289/// #     module
290/// # }
291/// ```
292///
293/// [`Result<T, E>`]: std::result::Result
294/// [`FunctionBuilder`]: crate::php::function::FunctionBuilder
295/// [`FromZval`]: crate::convert::FromZval
296/// [`IntoZval`]: crate::convert::IntoZval
297/// [`Zval`]: crate::types::Zval.
298/// [`Binary<T>`]: crate::binary::Binary
299/// [`ZendCallable`]: crate::types::ZendCallable
300/// [`PhpException`]: crate::exception::PhpException
301pub use ext_php_rs_derive::php_function;
302
303/// Annotates a structs `impl` block, declaring that all methods and constants
304/// declared inside the `impl` block will be declared as PHP methods and
305/// constants.
306///
307/// If you do not want to export a method to PHP, declare it in another `impl`
308/// block that is not tagged with this macro.
309///
310/// The declared methods and functions are kept intact so they can continue to
311/// be called from Rust. Methods do generate an additional function, with an
312/// identifier in the format `_internal_php_#ident`.
313///
314/// Methods and constants are declared mostly the same as their global
315/// counterparts, so read the documentation on the [`macro@php_function`] and
316/// [`macro@php_const`] macros for more details.
317///
318/// The main difference is that the contents of the `impl` block *do not* need
319/// to be tagged with additional attributes - this macro assumes that all
320/// contents of the `impl` block are to be exported to PHP.
321///
322/// The only contrary to this is setting the visibility, optional argument and
323/// default arguments for methods. These are done through separate macros:
324///
325/// - `#[defaults(key = value, ...)]` for setting defaults of method variables,
326///   similar to the
327///   function macro. Arguments with defaults need to be optional.
328/// - `#[optional(key)]` for setting `key` as an optional argument (and
329///   therefore the rest of the
330///   arguments).
331/// - `#[public]`, `#[protected]` and `#[private]` for setting the visibility of
332///   the method,
333///   defaulting to public. The Rust visibility has no effect on the PHP
334///   visibility.
335///
336/// Methods can take a immutable or a mutable reference to `self`, but cannot
337/// consume `self`. They can also take no reference to `self` which indicates a
338/// static method.
339///
340/// ## Constructors
341///
342/// You may add *one* constructor to the impl block. This method must be called
343/// `__construct` or be tagged with the `#[constructor]` attribute, and it will
344/// not be exported to PHP like a regular method.
345///
346/// The constructor method must not take a reference to `self` and must return
347/// `Self` or [`Result<Self, E>`][`Result`], where `E: Into<PhpException>`.
348///
349/// # Example
350///
351/// ```no_run
352/// # #![cfg_attr(windows, feature(abi_vectorcall))]
353/// # use ext_php_rs::prelude::*;
354/// #[php_class]
355/// #[derive(Debug)]
356/// pub struct Human {
357///     name: String,
358///     age: i32,
359/// }
360///
361/// #[php_impl]
362/// impl Human {
363///     // Class constant - `Human::AGE_LIMIT`
364///     const AGE_LIMIT: i32 = 100;
365///
366///     #[optional(age)]
367///     #[defaults(age = 0)]
368///     pub fn __construct(name: String, age: i32) -> Self {
369///         Self { name, age }
370///     }
371///
372///     pub fn get_name(&self) -> String {
373///         self.name.clone()
374///     }
375///
376///     pub fn get_age(&self) -> i32 {
377///         self.age
378///     }
379///
380///     // Static method - `Human::get_age_limit()`
381///     pub fn get_age_limit() -> i32 {
382///         Self::AGE_LIMIT
383///     }
384/// }
385///
386/// #[php_module]
387/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
388///     module
389/// }
390/// ```
391pub use ext_php_rs_derive::php_impl;
392
393/// Annotates a function that will be used by PHP to retrieve information about
394/// the module.
395///
396/// In the process, the function is wrapped by an `extern "C"` function which is
397/// called from PHP, which then calls the given function.
398///
399/// As well as wrapping the function, the `ModuleBuilder` is initialized and
400/// functions which have already been declared with the [`macro@php_function`]
401/// attribute will be registered with the module, so ideally you won't have to
402/// do anything inside the function.
403///
404/// The attribute must be called on a function *last*, i.e. the last proc-macro
405/// to be compiled, as the attribute relies on all other PHP attributes being
406/// compiled before the module. If another PHP attribute is compiled after the
407/// module attribute, an error will be thrown.
408///
409/// Note that if the function is not called `get_module`, it will be renamed.
410///
411/// If you have defined classes using the [`macro@php_class`] macro and you have
412/// not defined a startup function, it will be automatically declared and
413/// registered.
414///
415/// # Example
416///
417/// The `get_module` function is required in every PHP extension. This is a bare
418/// minimum example, since the function is declared above the module it will
419/// automatically be registered when the module attribute is called.
420///
421/// ```
422/// # #![cfg_attr(windows, feature(abi_vectorcall))]
423/// # use ext_php_rs::prelude::*;
424/// #[php_function]
425/// pub fn hello(name: String) -> String {
426///     format!("Hello, {}!", name)
427/// }
428///
429/// #[php_module]
430/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
431///     module
432/// }
433/// ```
434pub use ext_php_rs_derive::php_module;
435
436/// Annotates a struct that will be exported to PHP as a class.
437///
438/// By default, the class cannot be constructed from PHP. You must add a
439/// constructor method in the [`macro@php_impl`] impl block to be able to
440/// construct the object from PHP.
441///
442/// This attribute takes a set of optional arguments:
443///
444/// * `name` - The name of the exported class, if it is different from the Rust
445///   struct name. This can be useful for namespaced classes, as you cannot
446///   place backslashes in Rust struct names.
447///
448/// Any struct that uses this attribute can also provide an optional set of
449/// extra attributes, used to modify the class. These attributes must be used
450/// **underneath** this attribute, as they are not valid Rust attributes, and
451/// instead are parsed by this attribute:
452///
453/// * `#[extends(ce)]` - Sets the parent class of this new class. Can only be
454///   used once, and `ce` may be any valid expression.
455/// * `#[implements(ce)]` - Implements an interface on the new class. Can be
456///   used multiple times, and `ce` may be any valid expression.
457///
458/// This attribute (and its associated structs) must be defined *above* the
459/// startup function (which is annotated by the [`macro@php_startup`] macro, or
460/// automatically generated just above the [`macro@php_module`] function).
461///
462/// Fields defined on the struct *are not* the same as PHP properties, and are
463/// only accessible from Rust.
464///
465/// # Example
466///
467/// Export a simple class called `Example`, with 3 Rust fields.
468///
469/// ```
470/// # #![cfg_attr(windows, feature(abi_vectorcall))]
471/// # use ext_php_rs::prelude::*;
472/// #[php_class]
473/// pub struct Example {
474///     x: i32,
475///     y: String,
476///     z: bool
477/// }
478///
479/// #[php_module]
480/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
481///     module
482/// }
483/// ```
484///
485/// Create a custom exception `RedisException` inside the namespace
486/// `Redis\Exception`:
487///
488/// ```
489/// # #![cfg_attr(windows, feature(abi_vectorcall))]
490/// # use ext_php_rs::prelude::*;
491/// use ext_php_rs::exception::PhpException;
492/// use ext_php_rs::zend::ce;
493///
494/// #[php_class(name = "Redis\\Exception\\RedisException")]
495/// #[extends(ce::exception())]
496/// pub struct Example;
497///
498/// #[php_function]
499/// pub fn throw_exception() -> Result<i32, PhpException> {
500///     Err(PhpException::from_class::<Example>("Bad things happen".into()))
501/// }
502///
503/// #[php_module]
504/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
505///     module
506/// }
507/// ```
508pub use ext_php_rs_derive::php_class;
509
510/// Annotates a function that will be called by PHP when the module starts up.
511/// Generally used to register classes and constants.
512///
513/// As well as annotating the function, any classes and constants that had been
514/// declared using the [`macro@php_class`], [`macro@php_const`] and
515/// [`macro@php_impl`] attributes will be registered inside this function.
516///
517/// This function *must* be declared before the [`macro@php_module`] function,
518/// as this function needs to be declared when building the module.
519///
520/// This function will automatically be generated if not already declared with
521/// this macro if you have registered any classes or constants when using the
522/// [`macro@php_module`] macro.
523///
524/// The attribute accepts one optional flag -- `#[php_startup(before)]` --
525/// which forces the annotated function to be called _before_ the other classes
526/// and constants are registered. By default the annotated function is called
527/// after these classes and constants are registered.
528///
529/// # Example
530///
531/// ```
532/// # #![cfg_attr(windows, feature(abi_vectorcall))]
533/// # use ext_php_rs::prelude::*;
534/// #[php_startup]
535/// pub fn startup_function() {
536///     // do whatever you need to do...
537/// }
538/// # #[php_module]
539/// # pub fn module(module: ModuleBuilder) -> ModuleBuilder {
540/// #     module
541/// # }
542/// ```
543pub use ext_php_rs_derive::php_startup;
544
545/// Derives the traits required to convert a struct or enum to and from a
546/// [`Zval`]. Both [`FromZval`] and [`IntoZval`] are implemented on types which
547/// use this macro.
548///
549/// # Structs
550///
551/// When the macro is used on a struct, the [`FromZendObject`] and
552/// [`IntoZendObject`] traits are also implemented, and will attempt to retrieve
553/// values for the struct fields from the objects properties. This can be useful
554/// when you expect some arbitrary object (of which the type does not matter),
555/// but you care about the value of the properties.
556///
557/// All properties must implement [`FromZval`] and [`IntoZval`] themselves.
558/// Generics are supported, however, a [`FromZval`] and [`IntoZval`] bound will
559/// be added. If one property cannot be retrieved from the object, the whole
560/// conversion will fail.
561///
562/// ## Examples
563///
564/// Basic example with some primitive PHP type.
565///
566/// ```
567/// # #![cfg_attr(windows, feature(abi_vectorcall))]
568/// # use ext_php_rs::prelude::*;
569/// #[derive(Debug, ZvalConvert)]
570/// pub struct ExampleStruct<'a> {
571///     a: i32,
572///     b: String,
573///     c: &'a str
574/// }
575///
576/// #[php_function]
577/// pub fn take_object(obj: ExampleStruct) {
578///     dbg!(obj);
579/// }
580///
581/// #[php_function]
582/// pub fn give_object() -> ExampleStruct<'static> {
583///     ExampleStruct {
584///         a: 5,
585///         b: "Hello, world!".into(),
586///         c: "Static string",
587///     }
588/// }
589/// ```
590///
591/// Can be used in PHP:
592///
593/// ```php
594/// $obj = (object) [
595///     'a' => 5,
596///     'b' => 'Hello, world!',
597///     'c' => 'asdf',
598/// ];
599/// take_object($obj);
600/// var_dump(give_object());
601/// ```
602///
603/// Another example involving generics:
604///
605/// ```
606/// # #![cfg_attr(windows, feature(abi_vectorcall))]
607/// # use ext_php_rs::prelude::*;
608/// #[derive(Debug, ZvalConvert)]
609/// pub struct CompareVals<T: PartialEq<i32>> {
610///     a: T,
611///     b: T
612/// }
613///
614/// #[php_function]
615/// pub fn take_object(obj: CompareVals<i32>) {
616///     dbg!(obj);
617/// }
618/// ```
619///
620/// # Enums
621///
622/// When the macro is used on an enum, the [`FromZval`] and [`IntoZval`]
623/// implementations will treat the enum as a tagged union with a mixed datatype.
624/// This allows you to accept two different types in a parameter, for example, a
625/// string and an integer.
626///
627/// The enum variants must not have named fields (i.e. not in the form of a
628/// struct), and must have exactly one field, the type to extract from the
629/// [`Zval`]. Optionally, the enum may have a single default, empty variant,
630/// which is used when the [`Zval`] did not contain any data to fill
631/// the other variants. This empty variant is equivalent to `null` in PHP.
632///
633/// The ordering of the enum variants is important, as the [`Zval`] contents is
634/// matched in order of the variants. For example, [`Zval::string`] will attempt
635/// to read a string from the [`Zval`], and if the [`Zval`] contains a long, the
636/// long will be converted to a string. If a string variant was placed above an
637/// integer variant in the enum, the integer would be converted into a
638/// string and passed as the string variant.
639///
640/// ## Examples
641///
642/// Basic example showing the importance of variant ordering and default field:
643///
644/// ```
645/// # #![cfg_attr(windows, feature(abi_vectorcall))]
646/// # use ext_php_rs::prelude::*;
647/// #[derive(Debug, ZvalConvert)]
648/// pub enum UnionExample<'a> {
649///     Long(u64), // Long
650///     ProperStr(&'a str), // Actual string - not a converted value
651///     ParsedStr(String), // Potentially parsed string, i.e. a double
652///     None // Zval did not contain anything that could be parsed above
653/// }
654///
655/// #[php_function]
656/// pub fn test_union(val: UnionExample) {
657///     dbg!(val);
658/// }
659///
660/// #[php_function]
661/// pub fn give_union() -> UnionExample<'static> {
662///     UnionExample::Long(5)
663/// }
664/// ```
665///
666/// Use in PHP:
667///
668/// ```php
669/// test_union(5); // UnionExample::Long(5)
670/// test_union("Hello, world!"); // UnionExample::ProperStr("Hello, world!")
671/// test_union(5.66666); // UnionExample::ParsedStr("5.6666")
672/// test_union(null); // UnionExample::None
673/// var_dump(give_union()); // int(5)
674/// ```
675///
676/// [`FromZval`]: crate::convert::FromZval
677/// [`IntoZval`]: crate::convert::IntoZval
678/// [`FromZendObject`]: crate::convert::FromZendObject
679/// [`IntoZendObject`]: crate::convert::IntoZendObject
680/// [`Zval`]: crate::types::Zval.
681/// [`Zval::string`]: crate::types::Zval.::string
682pub use ext_php_rs_derive::ZvalConvert;
683
684/// Defines an `extern` function with the Zend fastcall convention based on
685/// operating system.
686///
687/// On Windows, Zend fastcall functions use the vector calling convention, while
688/// on all other operating systems no fastcall convention is used (just the
689/// regular C calling convention).
690///
691/// This macro wraps a function and applies the correct calling convention.
692///
693/// ## Examples
694///
695/// ```
696/// # #![cfg_attr(windows, feature(abi_vectorcall))]
697/// use ext_php_rs::zend_fastcall;
698///
699/// zend_fastcall! {
700///     pub extern fn test_hello_world(a: i32, b: i32) -> i32 {
701///         a + b
702///     }
703/// }
704/// ```
705///
706/// On Windows, this function will have the signature `pub extern "vectorcall"
707/// fn(i32, i32) -> i32`, while on macOS/Linux the function will have the
708/// signature `pub extern "C" fn(i32, i32) -> i32`.
709///
710/// ## Support
711///
712/// The `vectorcall` ABI is currently only supported on Windows with nightly
713/// Rust and the `abi_vectorcall` feature enabled.
714pub use ext_php_rs_derive::zend_fastcall;