ext_php_rs_derive/
lib.rs

1//! Macros for the `php-ext` crate.
2#![allow(clippy::needless_continue)] // TODO: Remove this once darling is updated to remove clippy issues
3mod class;
4mod constant;
5mod enum_;
6mod extern_;
7mod fastcall;
8mod function;
9mod helpers;
10mod impl_;
11mod interface;
12mod module;
13mod parsing;
14mod syn_ext;
15mod zval;
16
17use proc_macro::TokenStream;
18use proc_macro2::TokenStream as TokenStream2;
19use syn::{
20    DeriveInput, ItemConst, ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemStruct, ItemTrait,
21};
22
23extern crate proc_macro;
24
25// BEGIN DOCS FROM classes.md
26/// # `#[php_class]` Attribute
27///
28/// Structs can be exported to PHP as classes with the `#[php_class]` attribute
29/// macro. This attribute derives the `RegisteredClass` trait on your struct, as
30/// well as registering the class to be registered with the `#[php_module]`
31/// macro.
32///
33/// ## Options
34///
35/// There are additional macros that modify the class. These macros **must** be
36/// placed underneath the `#[php_class]` attribute.
37///
38/// - `name` - Changes the name of the class when exported to PHP. The Rust
39///   struct name is kept the same. If no name is given, the name of the struct
40///   is used. Useful for namespacing classes.
41/// - `change_case` - Changes the case of the class name when exported to PHP.
42/// - `#[php(extends(ce = ce_fn, stub = "ParentClass"))]` - Sets the parent
43///   class of the class. Can only be used once. `ce_fn` must be a function with
44///   the signature `fn() -> &'static ClassEntry`.
45/// - `#[php(implements(ce = ce_fn, stub = "InterfaceName"))]` - Implements the
46///   given interface on the class. Can be used multiple times. `ce_fn` must be
47///   a valid function with the signature `fn() -> &'static ClassEntry`.
48///
49/// You may also use the `#[php(prop)]` attribute on a struct field to use the
50/// field as a PHP property. By default, the field will be accessible from PHP
51/// publicly with the same name as the field. Property types must implement
52/// `IntoZval` and `FromZval`.
53///
54/// You can customize properties with these options:
55///
56/// - `name` - Allows you to rename the property, e.g. `#[php(prop, name =
57///   "new_name")]`
58/// - `change_case` - Allows you to rename the property using rename rules, e.g.
59///   `#[php(prop, change_case = PascalCase)]`
60/// - `static` - Makes the property static (shared across all instances), e.g.
61///   `#[php(prop, static)]`
62/// - `flags` - Sets property visibility flags, e.g. `#[php(prop, flags =
63///   ext_php_rs::flags::PropertyFlags::Private)]`
64///
65/// ## Restrictions
66///
67/// ### No lifetime parameters
68///
69/// Rust lifetimes are used by the Rust compiler to reason about a program's
70/// memory safety. They are a compile-time only concept;
71/// there is no way to access Rust lifetimes at runtime from a dynamic language
72/// like PHP.
73///
74/// As soon as Rust data is exposed to PHP,
75/// there is no guarantee which the Rust compiler can make on how long the data
76/// will live. PHP is a reference-counted language and those references can be
77/// held for an arbitrarily long time, which is untraceable by the Rust
78/// compiler. The only possible way to express this correctly is to require that
79/// any `#[php_class]` does not borrow data for any lifetime shorter than the
80/// `'static` lifetime, i.e. the `#[php_class]` cannot have any lifetime
81/// parameters.
82///
83/// When you need to share ownership of data between PHP and Rust,
84/// instead of using borrowed references with lifetimes, consider using
85/// reference-counted smart pointers such as [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html).
86///
87/// ### No generic parameters
88///
89/// A Rust struct `Foo<T>` with a generic parameter `T` generates new compiled
90/// implementations each time it is used with a different concrete type for `T`.
91/// These new implementations are generated by the compiler at each usage site.
92/// This is incompatible with wrapping `Foo` in PHP,
93/// where there needs to be a single compiled implementation of `Foo` which is
94/// integrated with the PHP interpreter.
95///
96/// ## Example
97///
98/// This example creates a PHP class `Human`, adding a PHP property `address`.
99///
100/// ```rust,no_run,ignore
101/// # #![cfg_attr(windows, feature(abi_vectorcall))]
102/// # extern crate ext_php_rs;
103/// use ext_php_rs::prelude::*;
104///
105/// #[php_class]
106/// pub struct Human {
107///     name: String,
108///     age: i32,
109///     #[php(prop)]
110///     address: String,
111/// }
112///
113/// #[php_module]
114/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
115///     module.class::<Human>()
116/// }
117/// # fn main() {}
118/// ```
119///
120/// Create a custom exception `RedisException`, which extends `Exception`, and
121/// put it in the `Redis\Exception` namespace:
122///
123/// ```rust,no_run,ignore
124/// # #![cfg_attr(windows, feature(abi_vectorcall))]
125/// # extern crate ext_php_rs;
126/// use ext_php_rs::{
127///     prelude::*,
128///     exception::PhpException,
129///     zend::ce
130/// };
131///
132/// #[php_class]
133/// #[php(name = "Redis\\Exception\\RedisException")]
134/// #[php(extends(ce = ce::exception, stub = "\\Exception"))]
135/// #[derive(Default)]
136/// pub struct RedisException;
137///
138/// // Throw our newly created exception
139/// #[php_function]
140/// pub fn throw_exception() -> PhpResult<i32> {
141///     Err(PhpException::from_class::<RedisException>("Not good!".into()))
142/// }
143///
144/// #[php_module]
145/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
146///     module
147///         .class::<RedisException>()
148///         .function(wrap_function!(throw_exception))
149/// }
150/// # fn main() {}
151/// ```
152///
153/// ## Implementing an Interface
154///
155/// To implement an interface, use `#[php(implements(ce = ce_fn, stub =
156/// "InterfaceName")]` where `ce_fn` is an function returning a `ClassEntry`. The following example implements [`ArrayAccess`](https://www.php.net/manual/en/class.arrayaccess.php):
157///
158/// ````rust,no_run,ignore
159/// # #![cfg_attr(windows, feature(abi_vectorcall))]
160/// # extern crate ext_php_rs;
161/// use ext_php_rs::{
162///     prelude::*,
163///     exception::PhpResult,
164///     types::Zval,
165///     zend::ce,
166/// };
167///
168/// #[php_class]
169/// #[php(implements(ce = ce::arrayaccess, stub = "\\ArrayAccess"))]
170/// #[derive(Default)]
171/// pub struct EvenNumbersArray;
172///
173/// /// Returns `true` if the array offset is an even number.
174/// /// Usage:
175/// /// ```php
176/// /// $arr = new EvenNumbersArray();
177/// /// var_dump($arr[0]); // true
178/// /// var_dump($arr[1]); // false
179/// /// var_dump($arr[2]); // true
180/// /// var_dump($arr[3]); // false
181/// /// var_dump($arr[4]); // true
182/// /// var_dump($arr[5] = true); // Fatal error:  Uncaught Exception: Setting values is not supported
183/// /// ```
184/// #[php_impl]
185/// impl EvenNumbersArray {
186///     pub fn __construct() -> EvenNumbersArray {
187///         EvenNumbersArray {}
188///     }
189///     // We need to use `Zval` because ArrayAccess needs $offset to be a `mixed`
190///     pub fn offset_exists(&self, offset: &'_ Zval) -> bool {
191///         offset.is_long()
192///     }
193///     pub fn offset_get(&self, offset: &'_ Zval) -> PhpResult<bool> {
194///         let integer_offset = offset.long().ok_or("Expected integer offset")?;
195///         Ok(integer_offset % 2 == 0)
196///     }
197///     pub fn offset_set(&mut self, _offset: &'_ Zval, _value: &'_ Zval) -> PhpResult {
198///         Err("Setting values is not supported".into())
199///     }
200///     pub fn offset_unset(&mut self, _offset: &'_ Zval) -> PhpResult {
201///         Err("Setting values is not supported".into())
202///     }
203/// }
204///
205/// #[php_module]
206/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
207///     module.class::<EvenNumbersArray>()
208/// }
209/// # fn main() {}
210/// ````
211///
212/// ## Static Properties
213///
214/// Static properties are shared across all instances of a class. Use
215/// `#[php(prop, static)]` to declare a static property. Unlike instance
216/// properties, static properties are managed entirely by PHP and do not use
217/// Rust property handlers.
218///
219/// You can specify a default value using the `default` attribute:
220///
221/// ```rust,no_run,ignore
222/// # #![cfg_attr(windows, feature(abi_vectorcall))]
223/// # extern crate ext_php_rs;
224/// use ext_php_rs::prelude::*;
225/// use ext_php_rs::class::RegisteredClass;
226///
227/// #[php_class]
228/// pub struct Counter {
229///     #[php(prop)]
230///     pub instance_value: i32,
231///     #[php(prop, static, default = 0)]
232///     pub count: i32,
233///     #[php(prop, static, flags = ext_php_rs::flags::PropertyFlags::Private)]
234///     pub internal_state: String,
235/// }
236///
237/// #[php_impl]
238/// impl Counter {
239///     pub fn __construct(value: i32) -> Self {
240///         Self {
241///             instance_value: value,
242///             count: 0,
243///             internal_state: String::new(),
244///         }
245///     }
246///
247///     /// Increment the static counter from Rust
248///     pub fn increment() {
249///         let ce = Self::get_metadata().ce();
250///         let current: i64 = ce.get_static_property("count").unwrap_or(0);
251///         ce.set_static_property("count", current + 1).unwrap();
252///     }
253///
254///     /// Get the current count
255///     pub fn get_count() -> i64 {
256///         let ce = Self::get_metadata().ce();
257///         ce.get_static_property("count").unwrap_or(0)
258///     }
259/// }
260///
261/// #[php_module]
262/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
263///     module.class::<Counter>()
264/// }
265/// # fn main() {}
266/// ```
267///
268/// From PHP, you can access static properties directly on the class:
269///
270/// ```php
271/// // No need to initialize - count already has default value of 0
272/// Counter::increment();
273/// Counter::increment();
274/// echo Counter::$count; // 2
275/// echo Counter::getCount(); // 2
276/// ```
277// END DOCS FROM classes.md
278#[proc_macro_attribute]
279pub fn php_class(args: TokenStream, input: TokenStream) -> TokenStream {
280    php_class_internal(args.into(), input.into()).into()
281}
282
283#[allow(clippy::needless_pass_by_value)]
284fn php_class_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
285    let input = parse_macro_input2!(input as ItemStruct);
286    if !args.is_empty() {
287        return err!(input => "`#[php_class(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
288    }
289
290    class::parser(input).unwrap_or_else(|e| e.to_compile_error())
291}
292
293// BEGIN DOCS FROM enum.md
294/// # `#[php_enum]` Attribute
295///
296/// Enums can be exported to PHP as enums with the `#[php_enum]` attribute
297/// macro. This attribute derives the `RegisteredClass` and `PhpEnum` traits on
298/// your enum. To register the enum use the `enumeration::<EnumName>()` method
299/// on the `ModuleBuilder` in the `#[php_module]` macro.
300///
301/// ## Options
302///
303/// The `#[php_enum]` attribute can be configured with the following options:
304/// - `#[php(name = "EnumName")]` or `#[php(change_case = snake_case)]`: Sets
305///   the name of the enum in PHP. The default is the `PascalCase` name of the
306///   enum.
307/// - `#[php(allow_native_discriminants)]`: Allows the use of native Rust
308///   discriminants (e.g., `Hearts = 1`).
309///
310/// The cases of the enum can be configured with the following options:
311/// - `#[php(name = "CaseName")]` or `#[php(change_case = snake_case)]`: Sets
312///   the name of the enum case in PHP. The default is the `PascalCase` name of
313///   the case.
314/// - `#[php(value = "value")]` or `#[php(value = 123)]`: Sets the discriminant
315///   value for the enum case. This can be a string or an integer. If not set,
316///   the case will be exported as a simple enum case without a discriminant.
317///
318/// ### Example
319///
320/// This example creates a PHP enum `Suit`.
321///
322/// ```rust,no_run,ignore
323/// # #![cfg_attr(windows, feature(abi_vectorcall))]
324/// # extern crate ext_php_rs;
325/// use ext_php_rs::prelude::*;
326///
327/// #[php_enum]
328/// pub enum Suit {
329///     Hearts,
330///     Diamonds,
331///     Clubs,
332///     Spades,
333/// }
334///
335/// #[php_module]
336/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
337///     module.enumeration::<Suit>()
338/// }
339/// # fn main() {}
340/// ```
341///
342/// ## Backed Enums
343/// Enums can also be backed by either `i64` or `&'static str`. Those values can
344/// be set using the `#[php(value = "value")]` or `#[php(value = 123)]`
345/// attributes on the enum variants.
346///
347/// All variants must have a value of the same type, either all `i64` or all
348/// `&'static str`.
349///
350/// ```rust,no_run,ignore
351/// # #![cfg_attr(windows, feature(abi_vectorcall))]
352/// # extern crate ext_php_rs;
353/// use ext_php_rs::prelude::*;
354///
355/// #[php_enum]
356/// pub enum Suit {
357///     #[php(value = "hearts")]
358///     Hearts,
359///     #[php(value = "diamonds")]
360///     Diamonds,
361///     #[php(value = "clubs")]
362///     Clubs,
363///     #[php(value = "spades")]
364///     Spades,
365/// }
366/// #[php_module]
367/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
368///     module.enumeration::<Suit>()
369/// }
370/// # fn main() {}
371/// ```
372///
373/// ### 'Native' Discriminators
374/// Native rust discriminants are currently not supported and will not be
375/// exported to PHP.
376///
377/// To avoid confusion a compiler error will be raised if you try to use a
378/// native discriminant. You can ignore this error by adding the
379/// `#[php(allow_native_discriminants)]` attribute to your enum.
380///
381/// ```rust,no_run,ignore
382/// # #![cfg_attr(windows, feature(abi_vectorcall))]
383/// # extern crate ext_php_rs;
384/// use ext_php_rs::prelude::*;
385///
386/// #[php_enum]
387/// #[php(allow_native_discriminants)]
388/// pub enum Suit {
389///     Hearts = 1,
390///     Diamonds = 2,
391///     Clubs = 3,
392///     Spades = 4,
393/// }
394///
395/// #[php_module]
396/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
397///     module.enumeration::<Suit>()
398/// }
399/// # fn main() {}
400/// ```
401///
402///
403/// TODO: Add backed enums example
404// END DOCS FROM enum.md
405#[proc_macro_attribute]
406pub fn php_enum(args: TokenStream, input: TokenStream) -> TokenStream {
407    php_enum_internal(args.into(), input.into()).into()
408}
409
410fn php_enum_internal(_args: TokenStream2, input: TokenStream2) -> TokenStream2 {
411    let input = parse_macro_input2!(input as ItemEnum);
412
413    enum_::parser(input).unwrap_or_else(|e| e.to_compile_error())
414}
415
416// BEGIN DOCS FROM interface.md
417/// # `#[php_interface]` Attribute
418///
419/// You can export a `Trait` block to PHP. This exports all methods as well as
420/// constants to PHP on the interface. Trait method SHOULD NOT contain default
421/// implementations, as these are not supported in PHP interfaces.
422///
423/// ## Options
424///
425/// By default all constants are renamed to `UPPER_CASE` and all methods are
426/// renamed to `camelCase`. This can be changed by passing the
427/// `change_method_case` and `change_constant_case` as `#[php]` attributes on
428/// the `impl` block. The options are:
429///
430/// - `#[php(change_method_case = "snake_case")]` - Renames the method to snake
431///   case.
432/// - `#[php(change_constant_case = "snake_case")]` - Renames the constant to
433///   snake case.
434///
435/// See the [`name` and `change_case`](./php.md#name-and-change_case) section
436/// for a list of all available cases.
437///
438/// ## Methods
439///
440/// See the [`php_impl`](./impl.md#)
441///
442/// ## Constants
443///
444/// See the [`php_impl`](./impl.md#)
445///
446/// ## Example
447///
448/// Define an example trait with methods and constant:
449///
450/// ```rust,no_run,ignore
451/// # #![cfg_attr(windows, feature(abi_vectorcall))]
452/// # extern crate ext_php_rs;
453/// use ext_php_rs::{prelude::*, types::ZendClassObject};
454///
455///
456/// #[php_interface]
457/// #[php(name = "Rust\\TestInterface")]
458/// trait Test {
459///     const TEST: &'static str = "TEST";
460///
461///     fn co();
462///
463///     #[php(defaults(value = 0))]
464///     fn set_value(&mut self, value: i32);
465/// }
466///
467/// #[php_module]
468/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
469///     module
470///         .interface::<PhpInterfaceTest>()
471/// }
472///
473/// # fn main() {}
474/// ```
475///
476/// Using our newly created interface in PHP:
477///
478/// ```php
479/// <?php
480///
481/// assert(interface_exists("Rust\TestInterface"));
482///
483/// class B implements Rust\TestInterface {
484///
485///     public static function co() {}
486///
487///     public function setValue(?int $value = 0) {
488///
489///     }
490/// }
491/// ```
492// END DOCS FROM interface.md
493#[proc_macro_attribute]
494pub fn php_interface(args: TokenStream, input: TokenStream) -> TokenStream {
495    php_interface_internal(args.into(), input.into()).into()
496}
497
498fn php_interface_internal(_args: TokenStream2, input: TokenStream2) -> TokenStream2 {
499    let input = parse_macro_input2!(input as ItemTrait);
500
501    interface::parser(input).unwrap_or_else(|e| e.to_compile_error())
502}
503
504// BEGIN DOCS FROM function.md
505/// # `#[php_function]` Attribute
506///
507/// Used to annotate functions which should be exported to PHP. Note that this
508/// should not be used on class methods - see the `#[php_impl]` macro for that.
509///
510/// See the [list of types](../types/index.md) that are valid as parameter and
511/// return types.
512///
513/// ## Optional parameters
514///
515/// Optional parameters can be used by setting the Rust parameter type to a
516/// variant of `Option<T>`. The macro will then figure out which parameters are
517/// optional by using the last consecutive arguments that are a variant of
518/// `Option<T>` or have a default value.
519///
520/// ```rust,no_run,ignore
521/// # #![cfg_attr(windows, feature(abi_vectorcall))]
522/// # extern crate ext_php_rs;
523/// use ext_php_rs::prelude::*;
524///
525/// #[php_function]
526/// pub fn greet(name: String, age: Option<i32>) -> String {
527///     let mut greeting = format!("Hello, {}!", name);
528///
529///     if let Some(age) = age {
530///         greeting += &format!(" You are {} years old.", age);
531///     }
532///
533///     greeting
534/// }
535///
536/// #[php_module]
537/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
538///     module.function(wrap_function!(greet))
539/// }
540/// # fn main() {}
541/// ```
542///
543/// Default parameter values can also be set for optional parameters. This is
544/// done through the `#[php(defaults)]` attribute option. When an optional
545/// parameter has a default, it does not need to be a variant of `Option`:
546///
547/// ```rust,no_run,ignore
548/// # #![cfg_attr(windows, feature(abi_vectorcall))]
549/// # extern crate ext_php_rs;
550/// use ext_php_rs::prelude::*;
551///
552/// #[php_function]
553/// #[php(defaults(offset = 0))]
554/// pub fn rusty_strpos(haystack: &str, needle: &str, offset: i64) -> Option<usize> {
555///     let haystack: String = haystack.chars().skip(offset as usize).collect();
556///     haystack.find(needle)
557/// }
558///
559/// #[php_module]
560/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
561///     module.function(wrap_function!(rusty_strpos))
562/// }
563/// # fn main() {}
564/// ```
565///
566/// Note that if there is a non-optional argument after an argument that is a
567/// variant of `Option<T>`, the `Option<T>` argument will be deemed a nullable
568/// argument rather than an optional argument.
569///
570/// ```rust,no_run,ignore
571/// # #![cfg_attr(windows, feature(abi_vectorcall))]
572/// # extern crate ext_php_rs;
573/// use ext_php_rs::prelude::*;
574///
575/// /// `age` will be deemed required and nullable rather than optional.
576/// #[php_function]
577/// pub fn greet(name: String, age: Option<i32>, description: String) -> String {
578///     let mut greeting = format!("Hello, {}!", name);
579///
580///     if let Some(age) = age {
581///         greeting += &format!(" You are {} years old.", age);
582///     }
583///
584///     greeting += &format!(" {}.", description);
585///     greeting
586/// }
587///
588/// #[php_module]
589/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
590///     module.function(wrap_function!(greet))
591/// }
592/// # fn main() {}
593/// ```
594///
595/// You can also specify the optional arguments if you want to have nullable
596/// arguments before optional arguments. This is done through an attribute
597/// parameter:
598///
599/// ```rust,no_run,ignore
600/// # #![cfg_attr(windows, feature(abi_vectorcall))]
601/// # extern crate ext_php_rs;
602/// use ext_php_rs::prelude::*;
603///
604/// /// `age` will be deemed required and nullable rather than optional,
605/// /// while description will be optional.
606/// #[php_function]
607/// #[php(optional = "description")]
608/// pub fn greet(name: String, age: Option<i32>, description: Option<String>) -> String {
609///     let mut greeting = format!("Hello, {}!", name);
610///
611///     if let Some(age) = age {
612///         greeting += &format!(" You are {} years old.", age);
613///     }
614///
615///     if let Some(description) = description {
616///         greeting += &format!(" {}.", description);
617///     }
618///
619///     greeting
620/// }
621///
622/// #[php_module]
623/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
624///     module.function(wrap_function!(greet))
625/// }
626/// # fn main() {}
627/// ```
628///
629/// ## Variadic Functions
630///
631/// Variadic functions can be implemented by specifying the last argument in the
632/// Rust function to the type `&[&Zval]`. This is the equivalent of a PHP
633/// function using the `...$args` syntax.
634///
635/// ```rust,no_run,ignore
636/// # #![cfg_attr(windows, feature(abi_vectorcall))]
637/// # extern crate ext_php_rs;
638/// use ext_php_rs::{prelude::*, types::Zval};
639///
640/// /// This can be called from PHP as `add(1, 2, 3, 4, 5)`
641/// #[php_function]
642/// pub fn add(number: u32, numbers:&[&Zval]) -> u32 {
643///     // numbers is a slice of 4 Zvals all of type long
644///     number
645/// }
646///
647/// #[php_module]
648/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
649///     module.function(wrap_function!(add))
650/// }
651/// # fn main() {}
652/// ```
653///
654/// ## Returning `Result<T, E>`
655///
656/// You can also return a `Result` from the function. The error variant will be
657/// translated into an exception and thrown. See the section on
658/// [exceptions](../exceptions.md) for more details.
659// END DOCS FROM function.md
660#[proc_macro_attribute]
661pub fn php_function(args: TokenStream, input: TokenStream) -> TokenStream {
662    php_function_internal(args.into(), input.into()).into()
663}
664
665#[allow(clippy::needless_pass_by_value)]
666fn php_function_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
667    let input = parse_macro_input2!(input as ItemFn);
668    if !args.is_empty() {
669        return err!(input => "`#[php_function(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
670    }
671
672    function::parser(input).unwrap_or_else(|e| e.to_compile_error())
673}
674
675// BEGIN DOCS FROM constant.md
676/// # `#[php_const]` Attribute
677///
678/// Exports a Rust constant as a global PHP constant. The constant can be any
679/// type that implements `IntoConst`.
680///
681/// The `wrap_constant!()` macro can be used to simplify the registration of
682/// constants. It sets the name and doc comments for the constant.
683///
684/// You can rename the const with options:
685///
686/// - `name` - Allows you to rename the property, e.g. `#[php(name =
687///   "new_name")]`
688/// - `change_case` - Allows you to rename the property using rename rules, e.g.
689///   `#[php(change_case = PascalCase)]`
690///
691/// ## Examples
692///
693/// ```rust,no_run,ignore
694/// # #![cfg_attr(windows, feature(abi_vectorcall))]
695/// # extern crate ext_php_rs;
696/// use ext_php_rs::prelude::*;
697///
698/// #[php_const]
699/// const TEST_CONSTANT: i32 = 100;
700///
701/// #[php_const]
702/// #[php(name = "I_AM_RENAMED")]
703/// const TEST_CONSTANT_THE_SECOND: i32 = 42;
704///
705/// #[php_const]
706/// const ANOTHER_STRING_CONST: &'static str = "Hello world!";
707///
708/// #[php_module]
709/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
710///     module
711///         .constant(wrap_constant!(TEST_CONSTANT))
712///         .constant(wrap_constant!(TEST_CONSTANT_THE_SECOND))
713///         .constant(("MANUAL_CONSTANT", ANOTHER_STRING_CONST, &[]))
714/// }
715/// # fn main() {}
716/// ```
717///
718/// ## PHP usage
719///
720/// ```php
721/// <?php
722///
723/// var_dump(TEST_CONSTANT); // int(100)
724/// var_dump(I_AM_RENAMED); // int(42)
725/// var_dump(MANUAL_CONSTANT); // string(12) "Hello world!"
726/// ```
727// END DOCS FROM constant.md
728#[proc_macro_attribute]
729pub fn php_const(args: TokenStream, input: TokenStream) -> TokenStream {
730    php_const_internal(args.into(), input.into()).into()
731}
732
733#[allow(clippy::needless_pass_by_value)]
734fn php_const_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
735    let input = parse_macro_input2!(input as ItemConst);
736    if !args.is_empty() {
737        return err!(input => "`#[php_const(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
738    }
739
740    constant::parser(input).unwrap_or_else(|e| e.to_compile_error())
741}
742
743// BEGIN DOCS FROM module.md
744/// # `#[php_module]` Attribute
745///
746/// The module macro is used to annotate the `get_module` function, which is
747/// used by the PHP interpreter to retrieve information about your extension,
748/// including the name, version, functions and extra initialization functions.
749/// Regardless if you use this macro, your extension requires a `extern "C" fn
750/// get_module()` so that PHP can get this information.
751///
752/// The function is renamed to `get_module` if you have used another name. The
753/// function is passed an instance of `ModuleBuilder` which allows you to
754/// register the following (if required):
755///
756/// - Functions, classes, and constants
757/// - Extension and request startup and shutdown functions.
758///   - Read more about the PHP extension lifecycle [here](https://www.phpinternalsbook.com/php7/extensions_design/php_lifecycle.html).
759/// - PHP extension information function
760///   - Used by the `phpinfo()` function to get information about your
761///     extension.
762///
763/// Classes and constants are not registered with PHP in the `get_module`
764/// function. These are registered inside the extension startup function.
765///
766/// ## Usage
767///
768/// ```rust,no_run,ignore
769/// # #![cfg_attr(windows, feature(abi_vectorcall))]
770/// # extern crate ext_php_rs;
771/// use ext_php_rs::{
772///     prelude::*,
773///     zend::ModuleEntry,
774///     info_table_start,
775///     info_table_row,
776///     info_table_end
777/// };
778///
779/// #[php_const]
780/// pub const MY_CUSTOM_CONST: &'static str = "Hello, world!";
781///
782/// #[php_class]
783/// pub struct Test {
784///     a: i32,
785///     b: i32
786/// }
787/// #[php_function]
788/// pub fn hello_world() -> &'static str {
789///     "Hello, world!"
790/// }
791///
792/// /// Used by the `phpinfo()` function and when you run `php -i`.
793/// /// This will probably be simplified with another macro eventually!
794/// pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
795///     info_table_start!();
796///     info_table_row!("my extension", "enabled");
797///     info_table_end!();
798/// }
799///
800/// #[php_module]
801/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
802///     module
803///         .constant(wrap_constant!(MY_CUSTOM_CONST))
804///         .class::<Test>()
805///         .function(wrap_function!(hello_world))
806///         .info_function(php_module_info)
807/// }
808/// # fn main() {}
809/// ```
810// END DOCS FROM module.md
811#[proc_macro_attribute]
812pub fn php_module(args: TokenStream, input: TokenStream) -> TokenStream {
813    php_module_internal(args.into(), input.into()).into()
814}
815
816#[allow(clippy::needless_pass_by_value)]
817fn php_module_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
818    let input = parse_macro_input2!(input as ItemFn);
819    if !args.is_empty() {
820        return err!(input => "`#[php_module(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
821    }
822
823    module::parser(input).unwrap_or_else(|e| e.to_compile_error())
824}
825
826// BEGIN DOCS FROM impl.md
827/// # `#[php_impl]` Attribute
828///
829/// You can export an entire `impl` block to PHP. This exports all methods as
830/// well as constants to PHP on the class that it is implemented on. This
831/// requires the `#[php_class]` macro to already be used on the underlying
832/// struct. Trait implementations cannot be exported to PHP. Only one `impl`
833/// block can be exported per class.
834///
835/// If you do not want a function exported to PHP, you should place it in a
836/// separate `impl` block.
837///
838/// If you want to use async Rust, use `#[php_async_impl]`, instead: see [here
839/// &raquo;](./async_impl.md) for more info.
840///
841/// ## Options
842///
843/// By default all constants are renamed to `UPPER_CASE` and all methods are
844/// renamed to camelCase. This can be changed by passing the
845/// `change_method_case` and `change_constant_case` as `#[php]` attributes on
846/// the `impl` block. The options are:
847///
848/// - `#[php(change_method_case = "snake_case")]` - Renames the method to snake
849///   case.
850/// - `#[php(change_constant_case = "snake_case")]` - Renames the constant to
851///   snake case.
852///
853/// See the [`name` and `change_case`](./php.md#name-and-change_case) section
854/// for a list of all available cases.
855///
856/// ## Methods
857///
858/// Methods basically follow the same rules as functions, so read about the
859/// [`php_function`] macro first. The primary difference between functions and
860/// methods is they are bounded by their class object.
861///
862/// Class methods can take a `&self` or `&mut self` parameter. They cannot take
863/// a consuming `self` parameter. Static methods can omit this `self` parameter.
864///
865/// To access the underlying Zend object, you can take a reference to a
866/// `ZendClassObject<T>` in place of the self parameter, where the parameter
867/// must be named `self_`. This can also be used to return a reference to
868/// `$this`.
869///
870/// The rest of the options are passed as separate attributes:
871///
872/// - `#[php(defaults(i = 5, b = "hello"))]` - Sets the default value for
873///   parameter(s).
874/// - `#[php(optional = i)]` - Sets the first optional parameter. Note that this
875///   also sets the remaining parameters as optional, so all optional parameters
876///   must be a variant of `Option<T>`.
877/// - `#[php(vis = "public")]`, `#[php(vis = "protected")]` and `#[php(vis =
878///   "private")]` - Sets the visibility of the method.
879/// - `#[php(name = "method_name")]` - Renames the PHP method to a different
880///   identifier, without renaming the Rust method name.
881///
882/// The `#[php(defaults)]` and `#[php(optional)]` attributes operate the same as
883/// the equivalent function attribute parameters.
884///
885/// ### Static Methods
886///
887/// Methods that do not take a `&self` or `&mut self` parameter are
888/// automatically exported as static methods. These can be called on the class
889/// itself without creating an instance.
890///
891/// ```rust,ignore
892/// #[php_impl]
893/// impl MyClass {
894///     // Static method - no self parameter
895///     pub fn create_default() -> Self {
896///         Self { /* ... */ }
897///     }
898///
899///     // Instance method - takes &self
900///     pub fn get_value(&self) -> i32 {
901///         self.value
902///     }
903/// }
904/// ```
905///
906/// From PHP:
907///
908/// ```php
909/// $obj = MyClass::createDefault(); // Static call
910/// $val = $obj->getValue();         // Instance call
911/// ```
912///
913/// ### Constructors
914///
915/// By default, if a class does not have a constructor, it is not constructable
916/// from PHP. It can only be returned from a Rust function to PHP.
917///
918/// Constructors are Rust methods which can take any amount of parameters and
919/// returns either `Self` or `Result<Self, E>`, where `E: Into<PhpException>`.
920/// When the error variant of `Result` is encountered, it is thrown as an
921/// exception and the class is not constructed.
922///
923/// Constructors are designated by either naming the method `__construct` or by
924/// annotating a method with the `#[php(constructor)]` attribute. Note that when
925/// using the attribute, the function is not exported to PHP like a regular
926/// method.
927///
928/// Constructors cannot use the visibility or rename attributes listed above.
929///
930/// ## Constants
931///
932/// Constants are defined as regular Rust `impl` constants. Any type that
933/// implements `IntoZval` can be used as a constant. Constant visibility is not
934/// supported at the moment, and therefore no attributes are valid on constants.
935///
936/// ## Property getters and setters
937///
938/// You can add properties to classes which use Rust functions as getters and/or
939/// setters. This is done with the `#[php(getter)]` and `#[php(setter)]`
940/// attributes. By default, the `get_` or `set_` prefix is trimmed from the
941/// start of the function name, and the remainder is used as the property name.
942///
943/// If you want to use a different name for the property, you can pass a `name`
944/// or `change_case` option to the `#[php]` attribute which will change the
945/// property name.
946///
947/// Properties do not necessarily have to have both a getter and a setter, if
948/// the property is immutable the setter can be omitted, and vice versa for
949/// getters.
950///
951/// The `#[php(getter)]` and `#[php(setter)]` attributes are mutually exclusive
952/// on methods. Properties cannot have multiple getters or setters, and the
953/// property name cannot conflict with field properties defined on the struct.
954///
955/// As the same as field properties, method property types must implement both
956/// `IntoZval` and `FromZval`.
957///
958/// ## Example
959///
960/// Continuing on from our `Human` example in the structs section, we will
961/// define a constructor, as well as getters for the properties. We will also
962/// define a constant for the maximum age of a `Human`.
963///
964/// ```rust,no_run,ignore
965/// # #![cfg_attr(windows, feature(abi_vectorcall))]
966/// # extern crate ext_php_rs;
967/// use ext_php_rs::{prelude::*, types::ZendClassObject};
968///
969/// #[php_class]
970/// #[derive(Debug, Default)]
971/// pub struct Human {
972///     name: String,
973///     age: i32,
974///     #[php(prop)]
975///     address: String,
976/// }
977///
978/// #[php_impl]
979/// impl Human {
980///     const MAX_AGE: i32 = 100;
981///
982///     // No `#[constructor]` attribute required here - the name is `__construct`.
983///     pub fn __construct(name: String, age: i32) -> Self {
984///         Self {
985///             name,
986///             age,
987///             address: String::new()
988///         }
989///     }
990///
991///     #[php(getter)]
992///     pub fn get_name(&self) -> String {
993///         self.name.to_string()
994///     }
995///
996///     #[php(setter)]
997///     pub fn set_name(&mut self, name: String) {
998///         self.name = name;
999///     }
1000///
1001///     #[php(getter)]
1002///     pub fn get_age(&self) -> i32 {
1003///         self.age
1004///     }
1005///
1006///     pub fn introduce(&self) {
1007///         println!("My name is {} and I am {} years old. I live at {}.", self.name, self.age, self.address);
1008///     }
1009///
1010///     pub fn get_raw_obj(self_: &mut ZendClassObject<Human>) -> &mut ZendClassObject<Human> {
1011///         dbg!(self_)
1012///     }
1013///
1014///     pub fn get_max_age() -> i32 {
1015///         Self::MAX_AGE
1016///     }
1017/// }
1018/// #[php_module]
1019/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1020///     module.class::<Human>()
1021/// }
1022/// # fn main() {}
1023/// ```
1024///
1025/// Using our newly created class in PHP:
1026///
1027/// ```php
1028/// <?php
1029///
1030/// $me = new Human('David', 20);
1031///
1032/// $me->introduce(); // My name is David and I am 20 years old.
1033/// var_dump(Human::get_max_age()); // int(100)
1034/// var_dump(Human::MAX_AGE); // int(100)
1035/// ```
1036///
1037/// [`php_async_impl`]: ./async_impl.md
1038// END DOCS FROM impl.md
1039#[proc_macro_attribute]
1040pub fn php_impl(args: TokenStream, input: TokenStream) -> TokenStream {
1041    php_impl_internal(args.into(), input.into()).into()
1042}
1043
1044#[allow(clippy::needless_pass_by_value)]
1045fn php_impl_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
1046    let input = parse_macro_input2!(input as ItemImpl);
1047    if !args.is_empty() {
1048        return err!(input => "`#[php_impl(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
1049    }
1050
1051    impl_::parser(input).unwrap_or_else(|e| e.to_compile_error())
1052}
1053
1054// BEGIN DOCS FROM extern.md
1055/// # `#[php_extern]` Attribute
1056///
1057/// Attribute used to annotate `extern` blocks which are deemed as PHP
1058/// functions.
1059///
1060/// This allows you to 'import' PHP functions into Rust so that they can be
1061/// called like regular Rust functions. Parameters can be any type that
1062/// implements [`IntoZval`], and the return type can be anything that implements
1063/// [`From<Zval>`] (notice how [`Zval`] is consumed rather than borrowed in this
1064/// case).
1065///
1066/// Unlike most other attributes, this does not need to be placed inside a
1067/// `#[php_module]` block.
1068///
1069/// # Panics
1070///
1071/// The function can panic when called under a few circumstances:
1072///
1073/// * The function could not be found or was not callable.
1074/// * One of the parameters could not be converted into a [`Zval`].
1075/// * The actual function call failed internally.
1076/// * The output [`Zval`] could not be parsed into the output type.
1077///
1078/// The last point can be important when interacting with functions that return
1079/// unions, such as [`strpos`] which can return an integer or a boolean. In this
1080/// case, a [`Zval`] should be returned as parsing a boolean to an integer is
1081/// invalid, and vice versa.
1082///
1083/// # Example
1084///
1085/// This `extern` block imports the [`strpos`] function from PHP. Notice that
1086/// the string parameters can take either [`String`] or [`&str`], the optional
1087/// parameter `offset` is an [`Option<i64>`], and the return value is a [`Zval`]
1088/// as the return type is an integer-boolean union.
1089///
1090/// ```rust,no_run,ignore
1091/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1092/// # extern crate ext_php_rs;
1093/// use ext_php_rs::{
1094///     prelude::*,
1095///     types::Zval,
1096/// };
1097///
1098/// #[php_extern]
1099/// extern "C" {
1100///     fn strpos(haystack: &str, needle: &str, offset: Option<i64>) -> Zval;
1101/// }
1102///
1103/// #[php_function]
1104/// pub fn my_strpos() {
1105///     assert_eq!(unsafe { strpos("Hello", "e", None) }.long(), Some(1));
1106/// }
1107///
1108/// #[php_module]
1109/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
1110///     module.function(wrap_function!(my_strpos))
1111/// }
1112/// # fn main() {}
1113/// ```
1114///
1115/// [`strpos`]: https://www.php.net/manual/en/function.strpos.php
1116/// [`IntoZval`]: crate::convert::IntoZval
1117/// [`Zval`]: crate::types::Zval
1118// END DOCS FROM extern.md
1119#[proc_macro_attribute]
1120pub fn php_extern(args: TokenStream, input: TokenStream) -> TokenStream {
1121    php_extern_internal(args.into(), input.into()).into()
1122}
1123
1124#[allow(clippy::needless_pass_by_value)]
1125fn php_extern_internal(_: TokenStream2, input: TokenStream2) -> TokenStream2 {
1126    let input = parse_macro_input2!(input as ItemForeignMod);
1127
1128    extern_::parser(input).unwrap_or_else(|e| e.to_compile_error())
1129}
1130
1131// BEGIN DOCS FROM zval_convert.md
1132/// # `ZvalConvert` Derive Macro
1133///
1134/// The `#[derive(ZvalConvert)]` macro derives the `FromZval` and `IntoZval`
1135/// traits on a struct or enum.
1136///
1137/// ## Structs
1138///
1139/// When used on a struct, the `FromZendObject` and `IntoZendObject` traits are
1140/// also implemented, mapping fields to properties in both directions. All
1141/// fields on the struct must implement `FromZval` as well. Generics are allowed
1142/// on structs that use the derive macro, however, the implementation will add a
1143/// `FromZval` bound to all generics types.
1144///
1145/// ### Examples
1146///
1147/// ```rust,no_run,ignore
1148/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1149/// # extern crate ext_php_rs;
1150/// use ext_php_rs::prelude::*;
1151///
1152/// #[derive(ZvalConvert)]
1153/// pub struct ExampleClass<'a> {
1154///     a: i32,
1155///     b: String,
1156///     c: &'a str
1157/// }
1158///
1159/// #[php_function]
1160/// pub fn take_object(obj: ExampleClass) {
1161///     dbg!(obj.a, obj.b, obj.c);
1162/// }
1163///
1164/// #[php_function]
1165/// pub fn give_object() -> ExampleClass<'static> {
1166///     ExampleClass {
1167///         a: 5,
1168///         b: "String".to_string(),
1169///         c: "Borrowed",
1170///     }
1171/// }
1172///
1173/// #[php_module]
1174/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1175///     module
1176///         .function(wrap_function!(take_object))
1177///         .function(wrap_function!(give_object))
1178/// }
1179/// # fn main() {}
1180/// ```
1181///
1182/// Calling from PHP:
1183///
1184/// ```php
1185/// <?php
1186///
1187/// $obj = new stdClass;
1188/// $obj->a = 5;
1189/// $obj->b = 'Hello, world!';
1190/// $obj->c = 'another string';
1191///
1192/// take_object($obj);
1193/// var_dump(give_object());
1194/// ```
1195///
1196/// Another example involving generics:
1197///
1198/// ```rust,no_run,ignore
1199/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1200/// # extern crate ext_php_rs;
1201/// use ext_php_rs::prelude::*;
1202///
1203/// // T must implement both `PartialEq<i32>` and `FromZval`.
1204/// #[derive(Debug, ZvalConvert)]
1205/// pub struct CompareVals<T: PartialEq<i32>> {
1206///     a: T,
1207///     b: T
1208/// }
1209///
1210/// #[php_function]
1211/// pub fn take_object(obj: CompareVals<i32>) {
1212///     dbg!(obj);
1213/// }
1214///
1215/// #[php_module]
1216/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1217///     module
1218///         .function(wrap_function!(take_object))
1219/// }
1220/// # fn main() {}
1221/// ```
1222///
1223/// ## Enums
1224///
1225/// When used on an enum, the `FromZval` implementation will treat the enum as a
1226/// tagged union with a mixed datatype. This allows you to accept multiple types
1227/// in a parameter, for example, a string and an integer.
1228///
1229/// The enum variants must not have named fields, and each variant must have
1230/// exactly one field (the type to extract from the zval). Optionally, the enum
1231/// may have one default variant with no data contained, which will be used when
1232/// the rest of the variants could not be extracted from the zval.
1233///
1234/// The ordering of the variants in the enum is important, as the `FromZval`
1235/// implementation will attempt to parse the zval data in order. For example, if
1236/// you put a `String` variant before an integer variant, the integer would be
1237/// converted to a string and passed as the string variant.
1238///
1239/// ### Examples
1240///
1241/// Basic example showing the importance of variant ordering and default field:
1242///
1243/// ```rust,no_run,ignore
1244/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1245/// # extern crate ext_php_rs;
1246/// use ext_php_rs::prelude::*;
1247///
1248/// #[derive(Debug, ZvalConvert)]
1249/// pub enum UnionExample<'a> {
1250///     Long(u64), // Long
1251///     ProperStr(&'a str), // Actual string - not a converted value
1252///     ParsedStr(String), // Potentially parsed string, i.e. a double
1253///     None // Zval did not contain anything that could be parsed above
1254/// }
1255///
1256/// #[php_function]
1257/// pub fn test_union(val: UnionExample) {
1258///     dbg!(val);
1259/// }
1260///
1261/// #[php_function]
1262/// pub fn give_union() -> UnionExample<'static> {
1263///     UnionExample::Long(5)
1264/// }
1265///
1266/// #[php_module]
1267/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1268///     module
1269///         .function(wrap_function!(test_union))
1270///         .function(wrap_function!(give_union))
1271/// }
1272/// # fn main() {}
1273/// ```
1274///
1275/// Use in PHP:
1276///
1277/// ```php
1278/// test_union(5); // UnionExample::Long(5)
1279/// test_union("Hello, world!"); // UnionExample::ProperStr("Hello, world!")
1280/// test_union(5.66666); // UnionExample::ParsedStr("5.6666")
1281/// test_union(null); // UnionExample::None
1282/// var_dump(give_union()); // int(5)
1283/// ```
1284// END DOCS FROM zval_convert.md
1285#[proc_macro_derive(ZvalConvert)]
1286pub fn zval_convert_derive(input: TokenStream) -> TokenStream {
1287    zval_convert_derive_internal(input.into()).into()
1288}
1289
1290fn zval_convert_derive_internal(input: TokenStream2) -> TokenStream2 {
1291    let input = parse_macro_input2!(input as DeriveInput);
1292
1293    zval::parser(input).unwrap_or_else(|e| e.to_compile_error())
1294}
1295
1296/// Defines an `extern` function with the Zend fastcall convention based on
1297/// operating system.
1298///
1299/// On Windows, Zend fastcall functions use the vector calling convention, while
1300/// on all other operating systems no fastcall convention is used (just the
1301/// regular C calling convention).
1302///
1303/// This macro wraps a function and applies the correct calling convention.
1304///
1305/// ## Examples
1306///
1307/// ```rust,ignore
1308/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1309/// use ext_php_rs::zend_fastcall;
1310///
1311/// zend_fastcall! {
1312///     pub extern fn test_hello_world(a: i32, b: i32) -> i32 {
1313///         a + b
1314///     }
1315/// }
1316/// ```
1317///
1318/// On Windows, this function will have the signature `pub extern "vectorcall"
1319/// fn(i32, i32) -> i32`, while on macOS/Linux the function will have the
1320/// signature `pub extern "C" fn(i32, i32) -> i32`.
1321///
1322/// ## Support
1323///
1324/// The `vectorcall` ABI is currently only supported on Windows with nightly
1325/// Rust and the `abi_vectorcall` feature enabled.
1326#[proc_macro]
1327pub fn zend_fastcall(input: TokenStream) -> TokenStream {
1328    zend_fastcall_internal(input.into()).into()
1329}
1330
1331fn zend_fastcall_internal(input: TokenStream2) -> TokenStream2 {
1332    let input = parse_macro_input2!(input as ItemFn);
1333
1334    fastcall::parser(input)
1335}
1336
1337/// Wraps a function to be used in the [`Module::function`] method.
1338#[proc_macro]
1339pub fn wrap_function(input: TokenStream) -> TokenStream {
1340    wrap_function_internal(input.into()).into()
1341}
1342
1343fn wrap_function_internal(input: TokenStream2) -> TokenStream2 {
1344    let input = parse_macro_input2!(input as syn::Path);
1345
1346    match function::wrap(&input) {
1347        Ok(parsed) => parsed,
1348        Err(e) => e.to_compile_error(),
1349    }
1350}
1351
1352/// Wraps a constant to be used in the [`ModuleBuilder::constant`] method.
1353#[proc_macro]
1354pub fn wrap_constant(input: TokenStream) -> TokenStream {
1355    wrap_constant_internal(input.into()).into()
1356}
1357
1358fn wrap_constant_internal(input: TokenStream2) -> TokenStream2 {
1359    let input = parse_macro_input2!(input as syn::Path);
1360
1361    match constant::wrap(&input) {
1362        Ok(parsed) => parsed,
1363        Err(e) => e.to_compile_error(),
1364    }
1365}
1366
1367macro_rules! parse_macro_input2 {
1368    ($tokenstream:ident as $ty:ty) => {
1369        match syn::parse2::<$ty>($tokenstream) {
1370            Ok(data) => data,
1371            Err(err) => {
1372                return proc_macro2::TokenStream::from(err.to_compile_error());
1373            }
1374        }
1375    };
1376    ($tokenstream:ident) => {
1377        $crate::parse_macro_input!($tokenstream as _)
1378    };
1379}
1380
1381pub(crate) use parse_macro_input2;
1382
1383macro_rules! err {
1384    ($span:expr => $($msg:tt)*) => {
1385        ::syn::Error::new(::syn::spanned::Spanned::span(&$span), format!($($msg)*))
1386    };
1387    ($($msg:tt)*) => {
1388        ::syn::Error::new(::proc_macro2::Span::call_site(), format!($($msg)*))
1389    };
1390}
1391
1392/// Bails out of a function with a syn error.
1393macro_rules! bail {
1394    ($span:expr => $($msg:tt)*) => {
1395        return Err($crate::err!($span => $($msg)*))
1396    };
1397    ($($msg:tt)*) => {
1398        return Err($crate::err!($($msg)*))
1399    };
1400}
1401
1402pub(crate) use bail;
1403pub(crate) use err;
1404
1405pub(crate) mod prelude {
1406    pub(crate) trait OptionTokens {
1407        fn option_tokens(&self) -> proc_macro2::TokenStream;
1408    }
1409
1410    impl<T: quote::ToTokens> OptionTokens for Option<T> {
1411        fn option_tokens(&self) -> proc_macro2::TokenStream {
1412            if let Some(token) = self {
1413                quote::quote! { ::std::option::Option::Some(#token) }
1414            } else {
1415                quote::quote! { ::std::option::Option::None }
1416            }
1417        }
1418    }
1419
1420    pub(crate) use crate::{bail, err};
1421    pub(crate) type Result<T> = std::result::Result<T, syn::Error>;
1422}
1423
1424#[cfg(test)]
1425mod tests {
1426    use super::*;
1427    use std::path::PathBuf;
1428
1429    type AttributeFn =
1430        fn(proc_macro2::TokenStream, proc_macro2::TokenStream) -> proc_macro2::TokenStream;
1431    type FunctionLikeFn = fn(proc_macro2::TokenStream) -> proc_macro2::TokenStream;
1432
1433    #[test]
1434    pub fn test_expand() {
1435        macrotest::expand("tests/expand/*.rs");
1436        for entry in glob::glob("tests/expand/*.rs").expect("Failed to read expand tests glob") {
1437            let entry = entry.expect("Failed to read expand test file");
1438            runtime_expand_attr(&entry);
1439            runtime_expand_func(&entry);
1440            runtime_expand_derive(&entry);
1441        }
1442    }
1443
1444    fn runtime_expand_attr(path: &PathBuf) {
1445        let file = std::fs::File::open(path).expect("Failed to open expand test file");
1446        runtime_macros::emulate_attributelike_macro_expansion(
1447            file,
1448            &[
1449                ("php_class", php_class_internal as AttributeFn),
1450                ("php_const", php_const_internal as AttributeFn),
1451                ("php_enum", php_enum_internal as AttributeFn),
1452                ("php_interface", php_interface_internal as AttributeFn),
1453                ("php_extern", php_extern_internal as AttributeFn),
1454                ("php_function", php_function_internal as AttributeFn),
1455                ("php_impl", php_impl_internal as AttributeFn),
1456                ("php_module", php_module_internal as AttributeFn),
1457            ],
1458        )
1459        .expect("Failed to expand attribute macros in test file");
1460    }
1461
1462    fn runtime_expand_func(path: &PathBuf) {
1463        let file = std::fs::File::open(path).expect("Failed to open expand test file");
1464        runtime_macros::emulate_functionlike_macro_expansion(
1465            file,
1466            &[
1467                ("zend_fastcall", zend_fastcall_internal as FunctionLikeFn),
1468                ("wrap_function", wrap_function_internal as FunctionLikeFn),
1469                ("wrap_constant", wrap_constant_internal as FunctionLikeFn),
1470            ],
1471        )
1472        .expect("Failed to expand function-like macros in test file");
1473    }
1474
1475    fn runtime_expand_derive(path: &PathBuf) {
1476        let file = std::fs::File::open(path).expect("Failed to open expand test file");
1477        runtime_macros::emulate_derive_macro_expansion(
1478            file,
1479            &[("ZvalConvert", zval_convert_derive_internal)],
1480        )
1481        .expect("Failed to expand derive macros in test file");
1482    }
1483}