Skip to main content

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 impl_interface;
12mod interface;
13mod module;
14mod parsing;
15mod syn_ext;
16mod zval;
17
18use darling::FromMeta;
19use proc_macro::TokenStream;
20use proc_macro2::TokenStream as TokenStream2;
21use syn::{
22    DeriveInput, ItemConst, ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemStruct, ItemTrait,
23};
24
25extern crate proc_macro;
26
27// BEGIN DOCS FROM classes.md
28/// # `#[php_class]` Attribute
29///
30/// Structs can be exported to PHP as classes with the `#[php_class]` attribute
31/// macro. This attribute derives the `RegisteredClass` trait on your struct, as
32/// well as registering the class to be registered with the `#[php_module]`
33/// macro.
34///
35/// ## Options
36///
37/// There are additional macros that modify the class. These macros **must** be
38/// placed underneath the `#[php_class]` attribute.
39///
40/// - `name` - Changes the name of the class when exported to PHP. The Rust
41///   struct name is kept the same. If no name is given, the name of the struct
42///   is used. Useful for namespacing classes.
43/// - `change_case` - Changes the case of the class name when exported to PHP.
44/// - `readonly` - Marks the class as readonly (PHP 8.2+). All properties in a
45///   readonly class are implicitly readonly.
46/// - `flags` - Sets class flags using `ClassFlags`, e.g. `#[php(flags =
47///   ClassFlags::Final)]` for a final class.
48/// - `#[php(extends(...))]` - Sets the parent class of the class. Can only be
49///   used once. Two forms are supported:
50///   - Simple type form: `#[php(extends(MyBaseClass))]` - For Rust-defined
51///     classes that implement `RegisteredClass`.
52///   - Explicit form: `#[php(extends(ce = ce_fn, stub = "ParentClass"))]` - For
53///     built-in PHP classes. `ce_fn` must be a function with the signature
54///     `fn() -> &'static ClassEntry`.
55/// - `#[php(implements(...))]` - Implements the given interface on the class.
56///   Can be used multiple times. Two forms are supported:
57///   - Simple type form: `#[php(implements(MyInterface))]` — For Rust-defined
58///     interfaces that implement `RegisteredClass`.
59///   - Explicit form: `#[php(implements(ce = ce_fn, stub = "InterfaceName"))]`
60///     — For built-in PHP interfaces. `ce_fn` must be a valid function with the
61///     signature `fn() -> &'static ClassEntry`.
62///
63/// You may also use the `#[php(prop)]` attribute on a struct field to use the
64/// field as a PHP property. By default, the field will be accessible from PHP
65/// publicly with the same name as the field. Property types must implement
66/// `IntoZval` and `FromZval`.
67///
68/// You can customize properties with these options:
69///
70/// - `name` - Allows you to rename the property, e.g. `#[php(prop, name =
71///   "new_name")]`
72/// - `change_case` - Allows you to rename the property using rename rules, e.g.
73///   `#[php(prop, change_case = PascalCase)]`
74/// - `static` - Makes the property static (shared across all instances), e.g.
75///   `#[php(prop, static)]`
76/// - `flags` - Sets property visibility flags, e.g. `#[php(prop, flags =
77///   ext_php_rs::flags::PropertyFlags::Private)]`
78///
79/// ## Restrictions
80///
81/// ### No lifetime parameters
82///
83/// Rust lifetimes are used by the Rust compiler to reason about a program's
84/// memory safety. They are a compile-time only concept;
85/// there is no way to access Rust lifetimes at runtime from a dynamic language
86/// like PHP.
87///
88/// As soon as Rust data is exposed to PHP,
89/// there is no guarantee which the Rust compiler can make on how long the data
90/// will live. PHP is a reference-counted language and those references can be
91/// held for an arbitrarily long time, which is untraceable by the Rust
92/// compiler. The only possible way to express this correctly is to require that
93/// any `#[php_class]` does not borrow data for any lifetime shorter than the
94/// `'static` lifetime, i.e. the `#[php_class]` cannot have any lifetime
95/// parameters.
96///
97/// When you need to share ownership of data between PHP and Rust,
98/// instead of using borrowed references with lifetimes, consider using
99/// reference-counted smart pointers such as [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html).
100///
101/// ### No generic parameters
102///
103/// A Rust struct `Foo<T>` with a generic parameter `T` generates new compiled
104/// implementations each time it is used with a different concrete type for `T`.
105/// These new implementations are generated by the compiler at each usage site.
106/// This is incompatible with wrapping `Foo` in PHP,
107/// where there needs to be a single compiled implementation of `Foo` which is
108/// integrated with the PHP interpreter.
109///
110/// ## Example
111///
112/// This example creates a PHP class `Human`, adding a PHP property `address`.
113///
114/// ```rust,no_run,ignore
115/// # #![cfg_attr(windows, feature(abi_vectorcall))]
116/// # extern crate ext_php_rs;
117/// use ext_php_rs::prelude::*;
118///
119/// #[php_class]
120/// pub struct Human {
121///     name: String,
122///     age: i32,
123///     #[php(prop)]
124///     address: String,
125/// }
126///
127/// #[php_module]
128/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
129///     module.class::<Human>()
130/// }
131/// # fn main() {}
132/// ```
133///
134/// Create a custom exception `RedisException`, which extends `Exception`, and
135/// put it in the `Redis\Exception` namespace:
136///
137/// ```rust,no_run,ignore
138/// # #![cfg_attr(windows, feature(abi_vectorcall))]
139/// # extern crate ext_php_rs;
140/// use ext_php_rs::{
141///     prelude::*,
142///     exception::PhpException,
143///     zend::ce
144/// };
145///
146/// #[php_class]
147/// #[php(name = "Redis\\Exception\\RedisException")]
148/// #[php(extends(ce = ce::exception, stub = "\\Exception"))]
149/// #[derive(Default)]
150/// pub struct RedisException;
151///
152/// // Throw our newly created exception
153/// #[php_function]
154/// pub fn throw_exception() -> PhpResult<i32> {
155///     Err(PhpException::from_class::<RedisException>("Not good!".into()))
156/// }
157///
158/// #[php_module]
159/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
160///     module
161///         .class::<RedisException>()
162///         .function(wrap_function!(throw_exception))
163/// }
164/// # fn main() {}
165/// ```
166///
167/// ### Extending a Rust-defined Class
168///
169/// When extending another Rust-defined class, you can use the simpler type
170/// syntax:
171///
172/// ```rust,ignore
173/// # #![cfg_attr(windows, feature(abi_vectorcall))]
174/// # extern crate ext_php_rs;
175/// use ext_php_rs::prelude::*;
176///
177/// #[php_class]
178/// #[derive(Default)]
179/// pub struct Animal;
180///
181/// #[php_impl]
182/// impl Animal {
183///     pub fn speak(&self) -> &'static str {
184///         "..."
185///     }
186/// }
187///
188/// #[php_class]
189/// #[php(extends(Animal))]
190/// #[derive(Default)]
191/// pub struct Dog;
192///
193/// #[php_impl]
194/// impl Dog {
195///     pub fn speak(&self) -> &'static str {
196///         "Woof!"
197///     }
198/// }
199///
200/// #[php_module]
201/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
202///     module
203///         .class::<Animal>()
204///         .class::<Dog>()
205/// }
206/// # fn main() {}
207/// ```
208///
209/// #### Sharing Methods Between Parent and Child Classes
210///
211/// When both parent and child are Rust-defined classes, methods defined only in
212/// the parent won't automatically work when called on a child instance. This is
213/// because each Rust type has its own object handlers.
214///
215/// The recommended workaround is to use a Rust trait for shared behavior:
216///
217/// ```rust,ignore
218/// # #![cfg_attr(windows, feature(abi_vectorcall))]
219/// # extern crate ext_php_rs;
220/// use ext_php_rs::prelude::*;
221///
222/// /// Trait for shared behavior
223/// trait AnimalBehavior {
224///     fn speak(&self) -> &'static str {
225///         "..."
226///     }
227/// }
228///
229/// #[php_class]
230/// #[derive(Default)]
231/// pub struct Animal;
232///
233/// impl AnimalBehavior for Animal {}
234///
235/// #[php_impl]
236/// impl Animal {
237///     pub fn speak(&self) -> &'static str {
238///         AnimalBehavior::speak(self)
239///     }
240/// }
241///
242/// #[php_class]
243/// #[php(extends(Animal))]
244/// #[derive(Default)]
245/// pub struct Dog;
246///
247/// impl AnimalBehavior for Dog {
248///     fn speak(&self) -> &'static str {
249///         "Woof!"
250///     }
251/// }
252///
253/// #[php_impl]
254/// impl Dog {
255///     // Re-export the method so it works on Dog instances
256///     pub fn speak(&self) -> &'static str {
257///         AnimalBehavior::speak(self)
258///     }
259/// }
260///
261/// #[php_module]
262/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
263///     module
264///         .class::<Animal>()
265///         .class::<Dog>()
266/// }
267/// # fn main() {}
268/// ```
269///
270/// This pattern ensures that:
271/// - `$animal->speak()` returns `"..."`
272/// - `$dog->speak()` returns `"Woof!"`
273/// - `$dog instanceof Animal` is `true`
274///
275/// ## Implementing an Interface
276///
277/// To implement an interface, use `#[php(implements(...))]`. For built-in PHP
278/// interfaces, use the explicit form with `ce` and `stub`. For Rust-defined
279/// interfaces, you can use the simple type form. The following example implements [`ArrayAccess`](https://www.php.net/manual/en/class.arrayaccess.php):
280///
281/// ````rust,no_run,ignore
282/// # #![cfg_attr(windows, feature(abi_vectorcall))]
283/// # extern crate ext_php_rs;
284/// use ext_php_rs::{
285///     prelude::*,
286///     exception::PhpResult,
287///     types::Zval,
288///     zend::ce,
289/// };
290///
291/// #[php_class]
292/// #[php(implements(ce = ce::arrayaccess, stub = "\\ArrayAccess"))]
293/// #[derive(Default)]
294/// pub struct EvenNumbersArray;
295///
296/// /// Returns `true` if the array offset is an even number.
297/// /// Usage:
298/// /// ```php
299/// /// $arr = new EvenNumbersArray();
300/// /// var_dump($arr[0]); // true
301/// /// var_dump($arr[1]); // false
302/// /// var_dump($arr[2]); // true
303/// /// var_dump($arr[3]); // false
304/// /// var_dump($arr[4]); // true
305/// /// var_dump($arr[5] = true); // Fatal error:  Uncaught Exception: Setting values is not supported
306/// /// ```
307/// #[php_impl]
308/// impl EvenNumbersArray {
309///     pub fn __construct() -> EvenNumbersArray {
310///         EvenNumbersArray {}
311///     }
312///     // We need to use `Zval` because ArrayAccess needs $offset to be a `mixed`
313///     pub fn offset_exists(&self, offset: &'_ Zval) -> bool {
314///         offset.is_long()
315///     }
316///     pub fn offset_get(&self, offset: &'_ Zval) -> PhpResult<bool> {
317///         let integer_offset = offset.long().ok_or("Expected integer offset")?;
318///         Ok(integer_offset % 2 == 0)
319///     }
320///     pub fn offset_set(&mut self, _offset: &'_ Zval, _value: &'_ Zval) -> PhpResult {
321///         Err("Setting values is not supported".into())
322///     }
323///     pub fn offset_unset(&mut self, _offset: &'_ Zval) -> PhpResult {
324///         Err("Setting values is not supported".into())
325///     }
326/// }
327///
328/// #[php_module]
329/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
330///     module.class::<EvenNumbersArray>()
331/// }
332/// # fn main() {}
333/// ````
334///
335/// ## Static Properties
336///
337/// Static properties are shared across all instances of a class. Use
338/// `#[php(prop, static)]` to declare a static property. Unlike instance
339/// properties, static properties are managed entirely by PHP and do not use
340/// Rust property handlers.
341///
342/// You can specify a default value using the `default` attribute:
343///
344/// ```rust,no_run,ignore
345/// # #![cfg_attr(windows, feature(abi_vectorcall))]
346/// # extern crate ext_php_rs;
347/// use ext_php_rs::prelude::*;
348/// use ext_php_rs::class::RegisteredClass;
349///
350/// #[php_class]
351/// pub struct Counter {
352///     #[php(prop)]
353///     pub instance_value: i32,
354///     #[php(prop, static, default = 0)]
355///     pub count: i32,
356///     #[php(prop, static, flags = ext_php_rs::flags::PropertyFlags::Private)]
357///     pub internal_state: String,
358/// }
359///
360/// #[php_impl]
361/// impl Counter {
362///     pub fn __construct(value: i32) -> Self {
363///         Self {
364///             instance_value: value,
365///             count: 0,
366///             internal_state: String::new(),
367///         }
368///     }
369///
370///     /// Increment the static counter from Rust
371///     pub fn increment() {
372///         let ce = Self::get_metadata().ce();
373///         let current: i64 = ce.get_static_property("count").unwrap_or(0);
374///         ce.set_static_property("count", current + 1).unwrap();
375///     }
376///
377///     /// Get the current count
378///     pub fn get_count() -> i64 {
379///         let ce = Self::get_metadata().ce();
380///         ce.get_static_property("count").unwrap_or(0)
381///     }
382/// }
383///
384/// #[php_module]
385/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
386///     module.class::<Counter>()
387/// }
388/// # fn main() {}
389/// ```
390///
391/// From PHP, you can access static properties directly on the class:
392///
393/// ```php
394/// // No need to initialize - count already has default value of 0
395/// Counter::increment();
396/// Counter::increment();
397/// echo Counter::$count; // 2
398/// echo Counter::getCount(); // 2
399/// ```
400///
401/// ## Abstract Classes
402///
403/// Abstract classes cannot be instantiated directly and may contain abstract
404/// methods that must be implemented by subclasses. Use `#[php(flags =
405/// ClassFlags::Abstract)]` to declare an abstract class:
406///
407/// ```rust,ignore
408/// use ext_php_rs::prelude::*;
409/// use ext_php_rs::flags::ClassFlags;
410///
411/// #[php_class]
412/// #[php(flags = ClassFlags::Abstract)]
413/// pub struct AbstractAnimal;
414///
415/// #[php_impl]
416/// impl AbstractAnimal {
417///     // Protected constructor for subclasses
418///     #[php(vis = "protected")]
419///     pub fn __construct() -> Self {
420///         Self
421///     }
422///
423///     // Abstract method - must be implemented by subclasses.
424///     // Body is never called; use unimplemented!() as a placeholder.
425///     #[php(abstract)]
426///     pub fn speak(&self) -> String {
427///         unimplemented!()
428///     }
429///
430///     // Concrete method - inherited by subclasses
431///     pub fn breathe(&self) {
432///         println!("Breathing...");
433///     }
434/// }
435/// ```
436///
437/// From PHP, you can extend this abstract class:
438///
439/// ```php
440/// class Dog extends AbstractAnimal {
441///     public function __construct() {
442///         parent::__construct();
443///     }
444///
445///     public function speak(): string {
446///         return "Woof!";
447///     }
448/// }
449///
450/// $dog = new Dog();
451/// echo $dog->speak(); // "Woof!"
452/// $dog->breathe();    // "Breathing..."
453///
454/// // This would cause an error:
455/// // $animal = new AbstractAnimal(); // Cannot instantiate abstract class
456/// ```
457///
458/// See the [impl documentation](./impl.md#abstract-methods) for more details on
459/// abstract methods.
460///
461/// ## Final Classes
462///
463/// Final classes cannot be extended. Use `#[php(flags = ClassFlags::Final)]` to
464/// declare a final class:
465///
466/// ```rust,ignore
467/// use ext_php_rs::prelude::*;
468/// use ext_php_rs::flags::ClassFlags;
469///
470/// #[php_class]
471/// #[php(flags = ClassFlags::Final)]
472/// pub struct FinalClass;
473/// ```
474///
475/// ## Readonly Classes (PHP 8.2+)
476///
477/// PHP 8.2 introduced [readonly classes](https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.readonly),
478/// where all properties are implicitly readonly. You can create a readonly
479/// class using the `#[php(readonly)]` attribute:
480///
481/// ```rust,ignore
482/// # #![cfg_attr(windows, feature(abi_vectorcall))]
483/// # extern crate ext_php_rs;
484/// use ext_php_rs::prelude::*;
485///
486/// #[php_class]
487/// #[php(readonly)]
488/// pub struct ImmutablePoint {
489///     x: f64,
490///     y: f64,
491/// }
492///
493/// #[php_impl]
494/// impl ImmutablePoint {
495///     pub fn __construct(x: f64, y: f64) -> Self {
496///         Self { x, y }
497///     }
498///
499///     pub fn get_x(&self) -> f64 {
500///         self.x
501///     }
502///
503///     pub fn get_y(&self) -> f64 {
504///         self.y
505///     }
506///
507///     pub fn distance_from_origin(&self) -> f64 {
508///         (self.x * self.x + self.y * self.y).sqrt()
509///     }
510/// }
511///
512/// #[php_module]
513/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
514///     module.class::<ImmutablePoint>()
515/// }
516/// # fn main() {}
517/// ```
518///
519/// From PHP:
520///
521/// ```php
522/// $point = new ImmutablePoint(3.0, 4.0);
523/// echo $point->getX(); // 3.0
524/// echo $point->getY(); // 4.0
525/// echo $point->distanceFromOrigin(); // 5.0
526///
527/// // On PHP 8.2+, you can verify the class is readonly:
528/// $reflection = new ReflectionClass(ImmutablePoint::class);
529/// var_dump($reflection->isReadOnly()); // true
530/// ```
531///
532/// The `readonly` attribute is compatible with other class attributes:
533///
534/// ```rust,ignore
535/// # #![cfg_attr(windows, feature(abi_vectorcall))]
536/// # extern crate ext_php_rs;
537/// use ext_php_rs::prelude::*;
538/// use ext_php_rs::flags::ClassFlags;
539///
540/// // Readonly + Final class
541/// #[php_class]
542/// #[php(readonly)]
543/// #[php(flags = ClassFlags::Final)]
544/// pub struct FinalImmutableData {
545///     value: String,
546/// }
547/// # fn main() {}
548/// ```
549///
550/// **Note:** The `readonly` attribute requires PHP 8.2 or later. Using it when
551/// compiling against an earlier PHP version will result in a compile error.
552///
553/// ### Conditional Compilation for Multi-Version Support
554///
555/// If your extension needs to support both PHP 8.1 and PHP 8.2+, you can use
556/// conditional compilation to only enable readonly on supported versions.
557///
558/// First, add `ext-php-rs-build` as a build dependency in your `Cargo.toml`:
559///
560/// ```toml
561/// [build-dependencies]
562/// ext-php-rs-build = "0.1"
563/// anyhow = "1"
564/// ```
565///
566/// Then create a `build.rs` that detects the PHP version and emits cfg flags:
567///
568/// ```rust,ignore
569/// use ext_php_rs_build::{find_php, PHPInfo, ApiVersion, emit_php_cfg_flags, emit_check_cfg};
570///
571/// fn main() -> anyhow::Result<()> {
572///     let php = find_php()?;
573///     let info = PHPInfo::get(&php)?;
574///     let version: ApiVersion = info.zend_version()?.try_into()?;
575///
576///     emit_check_cfg();
577///     emit_php_cfg_flags(version);
578///     Ok(())
579/// }
580/// ```
581///
582/// Now you can use `#[cfg(php82)]` to conditionally apply the readonly
583/// attribute:
584///
585/// ```rust,ignore
586/// #[php_class]
587/// #[cfg_attr(php82, php(readonly))]
588/// pub struct MaybeReadonlyClass {
589///     value: String,
590/// }
591/// ```
592///
593/// The `ext-php-rs-build` crate provides several useful utilities:
594///
595/// - `find_php()` - Locates the PHP executable (respects the `PHP` env var)
596/// - `PHPInfo::get()` - Runs `php -i` and parses the output
597/// - `ApiVersion` - Enum representing PHP versions (Php80, Php81, Php82, etc.)
598/// - `emit_php_cfg_flags()` - Emits `cargo:rustc-cfg=phpXX` for all supported
599///   versions
600/// - `emit_check_cfg()` - Emits check-cfg to avoid unknown cfg warnings
601///
602/// This is **optional** - if your extension only targets PHP 8.2+, you can use
603/// `#[php(readonly)]` directly without any build script setup.
604///
605/// ## Implementing Iterator
606///
607/// To make a Rust class usable with PHP's `foreach` loop, implement the
608/// [`Iterator`](https://www.php.net/manual/en/class.iterator.php) interface.
609/// This requires implementing five methods: `current()`, `key()`, `next()`,
610/// `rewind()`, and `valid()`.
611///
612/// The following example creates a `RangeIterator` that iterates over a range
613/// of integers:
614///
615/// ````rust,no_run,ignore
616/// # #![cfg_attr(windows, feature(abi_vectorcall))]
617/// # extern crate ext_php_rs;
618/// use ext_php_rs::{prelude::*, zend::ce};
619///
620/// #[php_class]
621/// #[php(implements(ce = ce::iterator, stub = "\\Iterator"))]
622/// pub struct RangeIterator {
623///     start: i64,
624///     end: i64,
625///     current: i64,
626///     index: i64,
627/// }
628///
629/// #[php_impl]
630/// impl RangeIterator {
631///     /// Create a new range iterator from start to end (inclusive).
632///     pub fn __construct(start: i64, end: i64) -> Self {
633///         Self {
634///             start,
635///             end,
636///             current: start,
637///             index: 0,
638///         }
639///     }
640///
641///     /// Return the current element.
642///     pub fn current(&self) -> i64 {
643///         self.current
644///     }
645///
646///     /// Return the key of the current element.
647///     pub fn key(&self) -> i64 {
648///         self.index
649///     }
650///
651///     /// Move forward to next element.
652///     pub fn next(&mut self) {
653///         self.current += 1;
654///         self.index += 1;
655///     }
656///
657///     /// Rewind the Iterator to the first element.
658///     pub fn rewind(&mut self) {
659///         self.current = self.start;
660///         self.index = 0;
661///     }
662///
663///     /// Checks if current position is valid.
664///     pub fn valid(&self) -> bool {
665///         self.current <= self.end
666///     }
667/// }
668///
669/// #[php_module]
670/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
671///     module.class::<RangeIterator>()
672/// }
673/// # fn main() {}
674/// ````
675///
676/// Using the iterator in PHP:
677///
678/// ```php
679/// <?php
680///
681/// $range = new RangeIterator(1, 5);
682///
683/// // Use with foreach
684/// foreach ($range as $key => $value) {
685///     echo "$key => $value\n";
686/// }
687/// // Output:
688/// // 0 => 1
689/// // 1 => 2
690/// // 2 => 3
691/// // 3 => 4
692/// // 4 => 5
693///
694/// // Works with iterator functions
695/// $arr = iterator_to_array(new RangeIterator(10, 12));
696/// // [0 => 10, 1 => 11, 2 => 12]
697///
698/// $count = iterator_count(new RangeIterator(1, 100));
699/// // 100
700/// ```
701///
702/// ### Iterator with Mixed Types
703///
704/// You can return different types for keys and values. The following example
705/// uses string keys:
706///
707/// ````rust,no_run,ignore
708/// # #![cfg_attr(windows, feature(abi_vectorcall))]
709/// # extern crate ext_php_rs;
710/// use ext_php_rs::{prelude::*, zend::ce};
711///
712/// #[php_class]
713/// #[php(implements(ce = ce::iterator, stub = "\\Iterator"))]
714/// pub struct MapIterator {
715///     keys: Vec<String>,
716///     values: Vec<String>,
717///     index: usize,
718/// }
719///
720/// #[php_impl]
721/// impl MapIterator {
722///     pub fn __construct() -> Self {
723///         Self {
724///             keys: vec!["first".into(), "second".into(), "third".into()],
725///             values: vec!["one".into(), "two".into(), "three".into()],
726///             index: 0,
727///         }
728///     }
729///
730///     pub fn current(&self) -> Option<String> {
731///         self.values.get(self.index).cloned()
732///     }
733///
734///     pub fn key(&self) -> Option<String> {
735///         self.keys.get(self.index).cloned()
736///     }
737///
738///     pub fn next(&mut self) {
739///         self.index += 1;
740///     }
741///
742///     pub fn rewind(&mut self) {
743///         self.index = 0;
744///     }
745///
746///     pub fn valid(&self) -> bool {
747///         self.index < self.keys.len()
748///     }
749/// }
750///
751/// #[php_module]
752/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
753///     module.class::<MapIterator>()
754/// }
755/// # fn main() {}
756/// ````
757///
758/// ```php
759/// <?php
760///
761/// $map = new MapIterator();
762/// foreach ($map as $key => $value) {
763///     echo "$key => $value\n";
764/// }
765/// // Output:
766/// // first => one
767/// // second => two
768/// // third => three
769/// ```
770// END DOCS FROM classes.md
771#[proc_macro_attribute]
772pub fn php_class(args: TokenStream, input: TokenStream) -> TokenStream {
773    php_class_internal(args.into(), input.into()).into()
774}
775
776#[allow(clippy::needless_pass_by_value)]
777fn php_class_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
778    let input = parse_macro_input2!(input as ItemStruct);
779    if !args.is_empty() {
780        return err!(input => "`#[php_class(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
781    }
782
783    class::parser(input).unwrap_or_else(|e| e.to_compile_error())
784}
785
786// BEGIN DOCS FROM enum.md
787/// # `#[php_enum]` Attribute
788///
789/// Enums can be exported to PHP as enums with the `#[php_enum]` attribute
790/// macro. This attribute derives the `RegisteredClass` and `PhpEnum` traits on
791/// your enum. To register the enum use the `enumeration::<EnumName>()` method
792/// on the `ModuleBuilder` in the `#[php_module]` macro.
793///
794/// ## Options
795///
796/// The `#[php_enum]` attribute can be configured with the following options:
797/// - `#[php(name = "EnumName")]` or `#[php(change_case = snake_case)]`: Sets
798///   the name of the enum in PHP. The default is the `PascalCase` name of the
799///   enum.
800/// - `#[php(allow_native_discriminants)]`: Allows the use of native Rust
801///   discriminants (e.g., `Hearts = 1`).
802///
803/// The cases of the enum can be configured with the following options:
804/// - `#[php(name = "CaseName")]` or `#[php(change_case = snake_case)]`: Sets
805///   the name of the enum case in PHP. The default is the `PascalCase` name of
806///   the case.
807/// - `#[php(value = "value")]` or `#[php(value = 123)]`: Sets the discriminant
808///   value for the enum case. This can be a string or an integer. If not set,
809///   the case will be exported as a simple enum case without a discriminant.
810///
811/// ### Example
812///
813/// This example creates a PHP enum `Suit`.
814///
815/// ```rust,no_run,ignore
816/// # #![cfg_attr(windows, feature(abi_vectorcall))]
817/// # extern crate ext_php_rs;
818/// use ext_php_rs::prelude::*;
819///
820/// #[php_enum]
821/// pub enum Suit {
822///     Hearts,
823///     Diamonds,
824///     Clubs,
825///     Spades,
826/// }
827///
828/// #[php_module]
829/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
830///     module.enumeration::<Suit>()
831/// }
832/// # fn main() {}
833/// ```
834///
835/// ## Backed Enums
836/// Enums can also be backed by either `i64` or `&'static str`. Those values can
837/// be set using the `#[php(value = "value")]` or `#[php(value = 123)]`
838/// attributes on the enum variants.
839///
840/// All variants must have a value of the same type, either all `i64` or all
841/// `&'static str`.
842///
843/// ```rust,no_run,ignore
844/// # #![cfg_attr(windows, feature(abi_vectorcall))]
845/// # extern crate ext_php_rs;
846/// use ext_php_rs::prelude::*;
847///
848/// #[php_enum]
849/// pub enum Suit {
850///     #[php(value = "hearts")]
851///     Hearts,
852///     #[php(value = "diamonds")]
853///     Diamonds,
854///     #[php(value = "clubs")]
855///     Clubs,
856///     #[php(value = "spades")]
857///     Spades,
858/// }
859/// #[php_module]
860/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
861///     module.enumeration::<Suit>()
862/// }
863/// # fn main() {}
864/// ```
865///
866/// ### 'Native' Discriminators
867/// Native rust discriminants are currently not supported and will not be
868/// exported to PHP.
869///
870/// To avoid confusion a compiler error will be raised if you try to use a
871/// native discriminant. You can ignore this error by adding the
872/// `#[php(allow_native_discriminants)]` attribute to your enum.
873///
874/// ```rust,no_run,ignore
875/// # #![cfg_attr(windows, feature(abi_vectorcall))]
876/// # extern crate ext_php_rs;
877/// use ext_php_rs::prelude::*;
878///
879/// #[php_enum]
880/// #[php(allow_native_discriminants)]
881/// pub enum Suit {
882///     Hearts = 1,
883///     Diamonds = 2,
884///     Clubs = 3,
885///     Spades = 4,
886/// }
887///
888/// #[php_module]
889/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
890///     module.enumeration::<Suit>()
891/// }
892/// # fn main() {}
893/// ```
894///
895///
896/// TODO: Add backed enums example
897// END DOCS FROM enum.md
898#[proc_macro_attribute]
899pub fn php_enum(args: TokenStream, input: TokenStream) -> TokenStream {
900    php_enum_internal(args.into(), input.into()).into()
901}
902
903fn php_enum_internal(_args: TokenStream2, input: TokenStream2) -> TokenStream2 {
904    let input = parse_macro_input2!(input as ItemEnum);
905
906    enum_::parser(input).unwrap_or_else(|e| e.to_compile_error())
907}
908
909// BEGIN DOCS FROM interface.md
910/// # `#[php_interface]` Attribute
911///
912/// You can export a `Trait` block to PHP. This exports all methods as well as
913/// constants to PHP on the interface. Trait method SHOULD NOT contain default
914/// implementations, as these are not supported in PHP interfaces.
915///
916/// ## Options
917///
918/// By default all constants are renamed to `UPPER_CASE` and all methods are
919/// renamed to `camelCase`. This can be changed by passing the
920/// `change_method_case` and `change_constant_case` as `#[php]` attributes on
921/// the `impl` block. The options are:
922///
923/// - `#[php(change_method_case = "snake_case")]` - Renames the method to snake
924///   case.
925/// - `#[php(change_constant_case = "snake_case")]` - Renames the constant to
926///   snake case.
927///
928/// See the [`name` and `change_case`](./php.md#name-and-change_case) section
929/// for a list of all available cases.
930///
931/// ## Methods
932///
933/// See the [`php_impl`](./impl.md#)
934///
935/// ## Constants
936///
937/// See the [`php_impl`](./impl.md#)
938///
939/// ## Example
940///
941/// Define an example trait with methods and constant:
942///
943/// ```rust,no_run,ignore
944/// # #![cfg_attr(windows, feature(abi_vectorcall))]
945/// # extern crate ext_php_rs;
946/// use ext_php_rs::{prelude::*, types::ZendClassObject};
947///
948///
949/// #[php_interface]
950/// #[php(name = "Rust\\TestInterface")]
951/// trait Test {
952///     const TEST: &'static str = "TEST";
953///
954///     fn co();
955///
956///     #[php(defaults(value = 0))]
957///     fn set_value(&mut self, value: i32);
958/// }
959///
960/// #[php_module]
961/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
962///     module
963///         .interface::<PhpInterfaceTest>()
964/// }
965///
966/// # fn main() {}
967/// ```
968///
969/// Using our newly created interface in PHP:
970///
971/// ```php
972/// <?php
973///
974/// assert(interface_exists("Rust\TestInterface"));
975///
976/// class B implements Rust\TestInterface {
977///
978///     public static function co() {}
979///
980///     public function setValue(?int $value = 0) {
981///
982///     }
983/// }
984/// ```
985///
986/// ## Interface Inheritance
987///
988/// PHP interfaces can extend other interfaces. You can achieve this in two
989/// ways:
990///
991/// ### Using `#[php(extends(...))]`
992///
993/// Use the `extends` attribute to extend a built-in PHP interface or another
994/// Rust-defined interface.
995///
996/// For built-in PHP interfaces, use the explicit form:
997///
998/// ```rust,no_run,ignore
999/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1000/// # extern crate ext_php_rs;
1001/// use ext_php_rs::prelude::*;
1002/// use ext_php_rs::zend::ce;
1003///
1004/// #[php_interface]
1005/// #[php(extends(ce = ce::throwable, stub = "\\Throwable"))]
1006/// #[php(name = "MyException")]
1007/// trait MyExceptionInterface {
1008///     fn get_error_code(&self) -> i32;
1009/// }
1010///
1011/// # fn main() {}
1012/// ```
1013///
1014/// For Rust-defined interfaces, you can use the simpler type syntax:
1015///
1016/// ```rust,ignore
1017/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1018/// # extern crate ext_php_rs;
1019/// use ext_php_rs::prelude::*;
1020///
1021/// #[php_interface]
1022/// trait BaseInterface {
1023///     fn base_method(&self) -> i32;
1024/// }
1025///
1026/// #[php_interface]
1027/// #[php(extends(BaseInterface))]
1028/// trait ExtendedInterface {
1029///     fn extended_method(&self) -> String;
1030/// }
1031///
1032/// # fn main() {}
1033/// ```
1034///
1035/// ### Using Rust Trait Bounds
1036///
1037/// You can also use Rust's trait bound syntax. When a trait marked with
1038/// `#[php_interface]` has supertraits, the PHP interface will automatically
1039/// extend those parent interfaces:
1040///
1041/// ```rust,no_run,ignore
1042/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1043/// # extern crate ext_php_rs;
1044/// use ext_php_rs::prelude::*;
1045///
1046/// #[php_interface]
1047/// #[php(name = "Rust\\ParentInterface")]
1048/// trait ParentInterface {
1049///     fn parent_method(&self) -> String;
1050/// }
1051///
1052/// // ChildInterface extends ParentInterface in PHP
1053/// #[php_interface]
1054/// #[php(name = "Rust\\ChildInterface")]
1055/// trait ChildInterface: ParentInterface {
1056///     fn child_method(&self) -> String;
1057/// }
1058///
1059/// #[php_module]
1060/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
1061///     module
1062///         .interface::<PhpInterfaceParentInterface>()
1063///         .interface::<PhpInterfaceChildInterface>()
1064/// }
1065///
1066/// # fn main() {}
1067/// ```
1068///
1069/// In PHP:
1070///
1071/// ```php
1072/// <?php
1073///
1074/// // ChildInterface extends ParentInterface
1075/// assert(is_a('Rust\ChildInterface', 'Rust\ParentInterface', true));
1076/// ```
1077///
1078/// # `#[php_impl_interface]` Attribute
1079///
1080/// The `#[php_impl_interface]` attribute allows a Rust class to implement a
1081/// custom PHP interface defined with `#[php_interface]`. This creates a
1082/// relationship where PHP's `instanceof` and `is_a()` recognize the
1083/// implementation.
1084///
1085/// **Key feature**: The macro automatically registers the trait methods as PHP
1086/// methods on the class. You don't need to duplicate them in a separate
1087/// `#[php_impl]` block.
1088///
1089/// ## Example
1090///
1091/// ```rust,no_run,ignore
1092/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1093/// # extern crate ext_php_rs;
1094/// use ext_php_rs::prelude::*;
1095///
1096/// // Define a custom interface
1097/// #[php_interface]
1098/// #[php(name = "Rust\\Greetable")]
1099/// trait Greetable {
1100///     fn greet(&self) -> String;
1101/// }
1102///
1103/// // Define a class
1104/// #[php_class]
1105/// #[php(name = "Rust\\Greeter")]
1106/// pub struct Greeter {
1107///     name: String,
1108/// }
1109///
1110/// #[php_impl]
1111/// impl Greeter {
1112///     pub fn __construct(name: String) -> Self {
1113///         Self { name }
1114///     }
1115///
1116///     // Note: No need to add greet() here - it's automatically
1117///     // registered by #[php_impl_interface] below
1118/// }
1119///
1120/// // Implement the interface for the class
1121/// // This automatically registers greet() as a PHP method
1122/// #[php_impl_interface]
1123/// impl Greetable for Greeter {
1124///     fn greet(&self) -> String {
1125///         format!("Hello, {}!", self.name)
1126///     }
1127/// }
1128///
1129/// #[php_module]
1130/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
1131///     module
1132///         .interface::<PhpInterfaceGreetable>()
1133///         .class::<Greeter>()
1134/// }
1135///
1136/// # fn main() {}
1137/// ```
1138///
1139/// Using in PHP:
1140///
1141/// ```php
1142/// <?php
1143///
1144/// $greeter = new Rust\Greeter("World");
1145///
1146/// // instanceof works
1147/// assert($greeter instanceof Rust\Greetable);
1148///
1149/// // is_a() works
1150/// assert(is_a($greeter, 'Rust\Greetable'));
1151///
1152/// // The greet() method is available (registered by #[php_impl_interface])
1153/// echo $greeter->greet(); // Output: Hello, World!
1154///
1155/// // Can be used as type hint
1156/// function greet(Rust\Greetable $obj): void {
1157///     echo $obj->greet();
1158/// }
1159///
1160/// greet($greeter);
1161/// ```
1162///
1163/// ## When to Use
1164///
1165/// - Use `#[php_impl_interface]` for custom interfaces you define with
1166///   `#[php_interface]`
1167/// - Use `#[php(implements(ce = ...))]` on `#[php_class]` for built-in PHP
1168///   interfaces like `Iterator`, `ArrayAccess`, `Countable`, etc.
1169///
1170/// See the [Classes documentation](./classes.md#implementing-an-interface) for
1171/// examples of implementing built-in interfaces.
1172///
1173/// ## Cross-Crate Support
1174///
1175/// The `#[php_impl_interface]` macro supports cross-crate interface discovery
1176/// via the [`inventory`](https://crates.io/crates/inventory) crate. This means you can define
1177/// an interface in one crate and implement it in another crate, and the
1178/// implementation will be automatically discovered at link time.
1179///
1180/// ### Example: Defining an Interface in a Library Crate
1181///
1182/// First, create a library crate that defines the interface:
1183///
1184/// ```toml
1185/// # my-interfaces/Cargo.toml
1186/// [package]
1187/// name = "my-interfaces"
1188/// version = "0.1.0"
1189///
1190/// [dependencies]
1191/// ext-php-rs = "0.15"
1192/// ```
1193///
1194/// ```rust,no_run,ignore,ignore
1195/// // my-interfaces/src/lib.rs
1196/// use ext_php_rs::prelude::*;
1197///
1198/// /// A serializable interface that can convert objects to JSON.
1199/// #[php_interface]
1200/// #[php(name = "MyInterfaces\\Serializable")]
1201/// pub trait Serializable {
1202///     fn to_json(&self) -> String;
1203/// }
1204///
1205/// // Re-export the generated PHP interface struct for consumers
1206/// pub use PhpInterfaceSerializable;
1207/// ```
1208///
1209/// ### Example: Implementing the Interface in Another Crate
1210///
1211/// Now create your extension crate that implements the interface:
1212///
1213/// ```toml
1214/// # my-extension/Cargo.toml
1215/// [package]
1216/// name = "my-extension"
1217/// version = "0.1.0"
1218///
1219/// [lib]
1220/// crate-type = ["cdylib"]
1221///
1222/// [dependencies]
1223/// ext-php-rs = "0.15"
1224/// my-interfaces = { path = "../my-interfaces" }
1225/// ```
1226///
1227/// ```rust,no_run,ignore,ignore
1228/// // my-extension/src/lib.rs
1229/// use ext_php_rs::prelude::*;
1230/// use my_interfaces::Serializable;
1231///
1232/// #[php_class]
1233/// #[php(name = "MyExtension\\User")]
1234/// pub struct User {
1235///     name: String,
1236///     email: String,
1237/// }
1238///
1239/// #[php_impl]
1240/// impl User {
1241///     pub fn __construct(name: String, email: String) -> Self {
1242///         Self { name, email }
1243///     }
1244///
1245///     // Note: No need to add to_json() here - it's automatically
1246///     // registered by #[php_impl_interface] below
1247/// }
1248///
1249/// // Register the interface implementation
1250/// // This automatically registers to_json() as a PHP method
1251/// #[php_impl_interface]
1252/// impl Serializable for User {
1253///     fn to_json(&self) -> String {
1254///         format!(r#"{{"name":"{}","email":"{}"}}"#, self.name, self.email)
1255///     }
1256/// }
1257///
1258/// #[php_module]
1259/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
1260///     module
1261///         // Register the interface from the library crate
1262///         .interface::<my_interfaces::PhpInterfaceSerializable>()
1263///         .class::<User>()
1264/// }
1265/// ```
1266///
1267/// ### Using in PHP
1268///
1269/// ```php
1270/// <?php
1271///
1272/// use MyExtension\User;
1273/// use MyInterfaces\Serializable;
1274///
1275/// $user = new User("John", "john@example.com");
1276///
1277/// // instanceof works across crates
1278/// assert($user instanceof Serializable);
1279///
1280/// // Type hints work
1281/// function serialize_object(Serializable $obj): string {
1282///     return $obj->toJson();
1283/// }
1284///
1285/// echo serialize_object($user);
1286/// // Output: {"name":"John","email":"john@example.com"}
1287/// ```
1288///
1289/// ### Important Notes
1290///
1291/// 1. **Automatic method registration**: The `#[php_impl_interface]` macro
1292///    automatically registers all trait methods as PHP methods on the class.
1293///    You don't need to duplicate them in a `#[php_impl]` block.
1294///
1295/// 2. **Interface registration**: The interface must be registered in the
1296///    `#[php_module]` function using `.interface::<PhpInterfaceName>()`.
1297///
1298/// 3. **Link-time discovery**: The `inventory` crate uses link-time
1299///    registration for interface discovery, so all implementations are
1300///    automatically discovered when the final binary is linked.
1301// END DOCS FROM interface.md
1302#[proc_macro_attribute]
1303pub fn php_interface(args: TokenStream, input: TokenStream) -> TokenStream {
1304    php_interface_internal(args.into(), input.into()).into()
1305}
1306
1307fn php_interface_internal(_args: TokenStream2, input: TokenStream2) -> TokenStream2 {
1308    let input = parse_macro_input2!(input as ItemTrait);
1309
1310    interface::parser(input).unwrap_or_else(|e| e.to_compile_error())
1311}
1312
1313// BEGIN DOCS FROM function.md
1314/// # `#[php_function]` Attribute
1315///
1316/// Used to annotate functions which should be exported to PHP. Note that this
1317/// should not be used on class methods - see the `#[php_impl]` macro for that.
1318///
1319/// See the [list of types](../types/index.md) that are valid as parameter and
1320/// return types.
1321///
1322/// ## Optional parameters
1323///
1324/// Optional parameters can be used by setting the Rust parameter type to a
1325/// variant of `Option<T>`. The macro will then figure out which parameters are
1326/// optional by using the last consecutive arguments that are a variant of
1327/// `Option<T>` or have a default value.
1328///
1329/// ```rust,no_run,ignore
1330/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1331/// # extern crate ext_php_rs;
1332/// use ext_php_rs::prelude::*;
1333///
1334/// #[php_function]
1335/// pub fn greet(name: String, age: Option<i32>) -> String {
1336///     let mut greeting = format!("Hello, {}!", name);
1337///
1338///     if let Some(age) = age {
1339///         greeting += &format!(" You are {} years old.", age);
1340///     }
1341///
1342///     greeting
1343/// }
1344///
1345/// #[php_module]
1346/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1347///     module.function(wrap_function!(greet))
1348/// }
1349/// # fn main() {}
1350/// ```
1351///
1352/// Default parameter values can also be set for optional parameters. This is
1353/// done through the `#[php(defaults)]` attribute option. When an optional
1354/// parameter has a default, it does not need to be a variant of `Option`:
1355///
1356/// ```rust,no_run,ignore
1357/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1358/// # extern crate ext_php_rs;
1359/// use ext_php_rs::prelude::*;
1360///
1361/// #[php_function]
1362/// #[php(defaults(offset = 0))]
1363/// pub fn rusty_strpos(haystack: &str, needle: &str, offset: i64) -> Option<usize> {
1364///     let haystack: String = haystack.chars().skip(offset as usize).collect();
1365///     haystack.find(needle)
1366/// }
1367///
1368/// #[php_module]
1369/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1370///     module.function(wrap_function!(rusty_strpos))
1371/// }
1372/// # fn main() {}
1373/// ```
1374///
1375/// Note that if there is a non-optional argument after an argument that is a
1376/// variant of `Option<T>`, the `Option<T>` argument will be deemed a nullable
1377/// argument rather than an optional argument.
1378///
1379/// ```rust,no_run,ignore
1380/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1381/// # extern crate ext_php_rs;
1382/// use ext_php_rs::prelude::*;
1383///
1384/// /// `age` will be deemed required and nullable rather than optional.
1385/// #[php_function]
1386/// pub fn greet(name: String, age: Option<i32>, description: String) -> String {
1387///     let mut greeting = format!("Hello, {}!", name);
1388///
1389///     if let Some(age) = age {
1390///         greeting += &format!(" You are {} years old.", age);
1391///     }
1392///
1393///     greeting += &format!(" {}.", description);
1394///     greeting
1395/// }
1396///
1397/// #[php_module]
1398/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1399///     module.function(wrap_function!(greet))
1400/// }
1401/// # fn main() {}
1402/// ```
1403///
1404/// You can also specify the optional arguments if you want to have nullable
1405/// arguments before optional arguments. This is done through an attribute
1406/// parameter:
1407///
1408/// ```rust,no_run,ignore
1409/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1410/// # extern crate ext_php_rs;
1411/// use ext_php_rs::prelude::*;
1412///
1413/// /// `age` will be deemed required and nullable rather than optional,
1414/// /// while description will be optional.
1415/// #[php_function]
1416/// #[php(optional = "description")]
1417/// pub fn greet(name: String, age: Option<i32>, description: Option<String>) -> String {
1418///     let mut greeting = format!("Hello, {}!", name);
1419///
1420///     if let Some(age) = age {
1421///         greeting += &format!(" You are {} years old.", age);
1422///     }
1423///
1424///     if let Some(description) = description {
1425///         greeting += &format!(" {}.", description);
1426///     }
1427///
1428///     greeting
1429/// }
1430///
1431/// #[php_module]
1432/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1433///     module.function(wrap_function!(greet))
1434/// }
1435/// # fn main() {}
1436/// ```
1437///
1438/// ## Variadic Functions
1439///
1440/// Variadic functions can be implemented by specifying the last argument in the
1441/// Rust function to the type `&[&Zval]`. This is the equivalent of a PHP
1442/// function using the `...$args` syntax.
1443///
1444/// ```rust,no_run,ignore
1445/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1446/// # extern crate ext_php_rs;
1447/// use ext_php_rs::{prelude::*, types::Zval};
1448///
1449/// /// This can be called from PHP as `add(1, 2, 3, 4, 5)`
1450/// #[php_function]
1451/// pub fn add(number: u32, numbers:&[&Zval]) -> u32 {
1452///     // numbers is a slice of 4 Zvals all of type long
1453///     number
1454/// }
1455///
1456/// #[php_module]
1457/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1458///     module.function(wrap_function!(add))
1459/// }
1460/// # fn main() {}
1461/// ```
1462///
1463/// ## Returning `Result<T, E>`
1464///
1465/// You can also return a `Result` from the function. The error variant will be
1466/// translated into an exception and thrown. See the section on
1467/// [exceptions](../exceptions.md) for more details.
1468// END DOCS FROM function.md
1469#[proc_macro_attribute]
1470pub fn php_function(args: TokenStream, input: TokenStream) -> TokenStream {
1471    php_function_internal(args.into(), input.into()).into()
1472}
1473
1474#[allow(clippy::needless_pass_by_value)]
1475fn php_function_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
1476    let input = parse_macro_input2!(input as ItemFn);
1477    if !args.is_empty() {
1478        return err!(input => "`#[php_function(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
1479    }
1480
1481    function::parser(input).unwrap_or_else(|e| e.to_compile_error())
1482}
1483
1484// BEGIN DOCS FROM constant.md
1485/// # `#[php_const]` Attribute
1486///
1487/// Exports a Rust constant as a global PHP constant. The constant can be any
1488/// type that implements `IntoConst`.
1489///
1490/// The `wrap_constant!()` macro can be used to simplify the registration of
1491/// constants. It sets the name and doc comments for the constant.
1492///
1493/// You can rename the const with options:
1494///
1495/// - `name` - Allows you to rename the property, e.g. `#[php(name =
1496///   "new_name")]`
1497/// - `change_case` - Allows you to rename the property using rename rules, e.g.
1498///   `#[php(change_case = PascalCase)]`
1499///
1500/// ## Examples
1501///
1502/// ```rust,no_run,ignore
1503/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1504/// # extern crate ext_php_rs;
1505/// use ext_php_rs::prelude::*;
1506///
1507/// #[php_const]
1508/// const TEST_CONSTANT: i32 = 100;
1509///
1510/// #[php_const]
1511/// #[php(name = "I_AM_RENAMED")]
1512/// const TEST_CONSTANT_THE_SECOND: i32 = 42;
1513///
1514/// #[php_const]
1515/// const ANOTHER_STRING_CONST: &'static str = "Hello world!";
1516///
1517/// #[php_module]
1518/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1519///     module
1520///         .constant(wrap_constant!(TEST_CONSTANT))
1521///         .constant(wrap_constant!(TEST_CONSTANT_THE_SECOND))
1522///         .constant(("MANUAL_CONSTANT", ANOTHER_STRING_CONST, &[]))
1523/// }
1524/// # fn main() {}
1525/// ```
1526///
1527/// ## PHP usage
1528///
1529/// ```php
1530/// <?php
1531///
1532/// var_dump(TEST_CONSTANT); // int(100)
1533/// var_dump(I_AM_RENAMED); // int(42)
1534/// var_dump(MANUAL_CONSTANT); // string(12) "Hello world!"
1535/// ```
1536// END DOCS FROM constant.md
1537#[proc_macro_attribute]
1538pub fn php_const(args: TokenStream, input: TokenStream) -> TokenStream {
1539    php_const_internal(args.into(), input.into()).into()
1540}
1541
1542#[allow(clippy::needless_pass_by_value)]
1543fn php_const_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
1544    let input = parse_macro_input2!(input as ItemConst);
1545    if !args.is_empty() {
1546        return err!(input => "`#[php_const(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
1547    }
1548
1549    constant::parser(input).unwrap_or_else(|e| e.to_compile_error())
1550}
1551
1552// BEGIN DOCS FROM module.md
1553/// # `#[php_module]` Attribute
1554///
1555/// The module macro is used to annotate the `get_module` function, which is
1556/// used by the PHP interpreter to retrieve information about your extension,
1557/// including the name, version, functions and extra initialization functions.
1558/// Regardless if you use this macro, your extension requires a `extern "C" fn
1559/// get_module()` so that PHP can get this information.
1560///
1561/// The function is renamed to `get_module` if you have used another name. The
1562/// function is passed an instance of `ModuleBuilder` which allows you to
1563/// register the following (if required):
1564///
1565/// - Functions, classes, and constants
1566/// - Extension and request startup and shutdown functions.
1567///   - Read more about the PHP extension lifecycle [here](https://www.phpinternalsbook.com/php7/extensions_design/php_lifecycle.html).
1568/// - PHP extension information function
1569///   - Used by the `phpinfo()` function to get information about your
1570///     extension.
1571///
1572/// Classes and constants are not registered with PHP in the `get_module`
1573/// function. These are registered inside the extension startup function.
1574///
1575/// ## Usage
1576///
1577/// ```rust,no_run,ignore
1578/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1579/// # extern crate ext_php_rs;
1580/// use ext_php_rs::{
1581///     prelude::*,
1582///     zend::ModuleEntry,
1583///     info_table_start,
1584///     info_table_row,
1585///     info_table_end
1586/// };
1587///
1588/// #[php_const]
1589/// pub const MY_CUSTOM_CONST: &'static str = "Hello, world!";
1590///
1591/// #[php_class]
1592/// pub struct Test {
1593///     a: i32,
1594///     b: i32
1595/// }
1596/// #[php_function]
1597/// pub fn hello_world() -> &'static str {
1598///     "Hello, world!"
1599/// }
1600///
1601/// /// Used by the `phpinfo()` function and when you run `php -i`.
1602/// /// This will probably be simplified with another macro eventually!
1603/// pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
1604///     info_table_start!();
1605///     info_table_row!("my extension", "enabled");
1606///     info_table_end!();
1607/// }
1608///
1609/// #[php_module]
1610/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1611///     module
1612///         .constant(wrap_constant!(MY_CUSTOM_CONST))
1613///         .class::<Test>()
1614///         .function(wrap_function!(hello_world))
1615///         .info_function(php_module_info)
1616/// }
1617/// # fn main() {}
1618/// ```
1619// END DOCS FROM module.md
1620#[proc_macro_attribute]
1621pub fn php_module(args: TokenStream, input: TokenStream) -> TokenStream {
1622    php_module_internal(args.into(), input.into()).into()
1623}
1624
1625#[allow(clippy::needless_pass_by_value)]
1626fn php_module_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
1627    let input = parse_macro_input2!(input as ItemFn);
1628    if !args.is_empty() {
1629        return err!(input => "`#[php_module(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
1630    }
1631
1632    module::parser(input).unwrap_or_else(|e| e.to_compile_error())
1633}
1634
1635// BEGIN DOCS FROM impl.md
1636/// # `#[php_impl]` Attribute
1637///
1638/// You can export an entire `impl` block to PHP. This exports all methods as
1639/// well as constants to PHP on the class that it is implemented on. This
1640/// requires the `#[php_class]` macro to already be used on the underlying
1641/// struct. Trait implementations cannot be exported to PHP. Only one `impl`
1642/// block can be exported per class.
1643///
1644/// If you do not want a function exported to PHP, you should place it in a
1645/// separate `impl` block.
1646///
1647/// If you want to use async Rust, use `#[php_async_impl]`, instead: see [here
1648/// &raquo;](./async_impl.md) for more info.
1649///
1650/// ## Options
1651///
1652/// By default all constants are renamed to `UPPER_CASE` and all methods are
1653/// renamed to camelCase. This can be changed by passing the
1654/// `change_method_case` and `change_constant_case` as `#[php]` attributes on
1655/// the `impl` block. The options are:
1656///
1657/// - `#[php(change_method_case = "snake_case")]` - Renames the method to snake
1658///   case.
1659/// - `#[php(change_constant_case = "snake_case")]` - Renames the constant to
1660///   snake case.
1661///
1662/// See the [`name` and `change_case`](./php.md#name-and-change_case) section
1663/// for a list of all available cases.
1664///
1665/// ## Methods
1666///
1667/// Methods basically follow the same rules as functions, so read about the
1668/// [`php_function`] macro first. The primary difference between functions and
1669/// methods is they are bounded by their class object.
1670///
1671/// Class methods can take a `&self` or `&mut self` parameter. They cannot take
1672/// a consuming `self` parameter. Static methods can omit this `self` parameter.
1673///
1674/// To access the underlying Zend object, you can take a reference to a
1675/// `ZendClassObject<T>` in place of the self parameter, where the parameter
1676/// must be named `self_`. This can also be used to return a reference to
1677/// `$this`.
1678///
1679/// The rest of the options are passed as separate attributes:
1680///
1681/// - `#[php(defaults(i = 5, b = "hello"))]` - Sets the default value for
1682///   parameter(s).
1683/// - `#[php(optional = i)]` - Sets the first optional parameter. Note that this
1684///   also sets the remaining parameters as optional, so all optional parameters
1685///   must be a variant of `Option<T>`.
1686/// - `#[php(vis = "public")]`, `#[php(vis = "protected")]` and `#[php(vis =
1687///   "private")]` - Sets the visibility of the method.
1688/// - `#[php(name = "method_name")]` - Renames the PHP method to a different
1689///   identifier, without renaming the Rust method name.
1690/// - `#[php(final)]` - Makes the method final (cannot be overridden in
1691///   subclasses).
1692/// - `#[php(abstract)]` - Makes the method abstract (must be implemented by
1693///   subclasses). Can only be used in abstract classes.
1694///
1695/// The `#[php(defaults)]` and `#[php(optional)]` attributes operate the same as
1696/// the equivalent function attribute parameters.
1697///
1698/// ### Static Methods
1699///
1700/// Methods that do not take a `&self` or `&mut self` parameter are
1701/// automatically exported as static methods. These can be called on the class
1702/// itself without creating an instance.
1703///
1704/// ```rust,ignore
1705/// #[php_impl]
1706/// impl MyClass {
1707///     // Static method - no self parameter
1708///     pub fn create_default() -> Self {
1709///         Self { /* ... */ }
1710///     }
1711///
1712///     // Instance method - takes &self
1713///     pub fn get_value(&self) -> i32 {
1714///         self.value
1715///     }
1716/// }
1717/// ```
1718///
1719/// From PHP:
1720///
1721/// ```php
1722/// $obj = MyClass::createDefault(); // Static call
1723/// $val = $obj->getValue();         // Instance call
1724/// ```
1725///
1726/// ### Final Methods
1727///
1728/// Methods marked with `#[php(final)]` cannot be overridden in subclasses. This
1729/// is useful when you want to prevent modification of critical functionality.
1730///
1731/// ```rust,ignore
1732/// use ext_php_rs::prelude::*;
1733///
1734/// #[php_class]
1735/// pub struct SecureClass;
1736///
1737/// #[php_impl]
1738/// impl SecureClass {
1739///     #[php(final)]
1740///     pub fn secure_method(&self) -> &str {
1741///         "This cannot be overridden"
1742///     }
1743///
1744///     // Final static methods are also supported
1745///     #[php(final)]
1746///     pub fn secure_static() -> i32 {
1747///         42
1748///     }
1749/// }
1750/// ```
1751///
1752/// ### Abstract Methods
1753///
1754/// Methods marked with `#[php(abstract)]` must be implemented by subclasses.
1755/// Abstract methods can only be defined in abstract classes (classes with
1756/// `ClassFlags::Abstract`).
1757///
1758/// ```rust,ignore
1759/// use ext_php_rs::prelude::*;
1760/// use ext_php_rs::flags::ClassFlags;
1761///
1762/// #[php_class]
1763/// #[php(flags = ClassFlags::Abstract)]
1764/// pub struct AbstractShape;
1765///
1766/// #[php_impl]
1767/// impl AbstractShape {
1768///     // Protected constructor for subclasses
1769///     #[php(vis = "protected")]
1770///     pub fn __construct() -> Self {
1771///         Self
1772///     }
1773///
1774///     // Abstract method - subclasses must implement this.
1775///     // The body is never called; use unimplemented!() as a placeholder.
1776///     #[php(abstract)]
1777///     pub fn area(&self) -> f64 {
1778///         unimplemented!()
1779///     }
1780///
1781///     // Concrete method in abstract class
1782///     pub fn describe(&self) -> String {
1783///         format!("A shape with area {}", self.area())
1784///     }
1785/// }
1786/// ```
1787///
1788/// **Note:** Abstract method bodies are never called - they exist only because
1789/// Rust syntax requires a body for methods in `impl` blocks. Use
1790/// `unimplemented!()` as a clear placeholder.
1791///
1792/// **Note:** If you try to use `#[php(abstract)]` on a method in a non-abstract
1793/// class, you will get a compile-time error.
1794///
1795/// **Note:** PHP does not support abstract static methods. If you need static
1796/// behavior that can be customized by subclasses, use a regular instance method
1797/// or the [late static binding](https://www.php.net/manual/en/language.oop5.late-static-bindings.php)
1798/// pattern in PHP.
1799///
1800/// ### Constructors
1801///
1802/// By default, if a class does not have a constructor, it is not constructable
1803/// from PHP. It can only be returned from a Rust function to PHP.
1804///
1805/// Constructors are Rust methods which can take any amount of parameters and
1806/// returns either `Self` or `Result<Self, E>`, where `E: Into<PhpException>`.
1807/// When the error variant of `Result` is encountered, it is thrown as an
1808/// exception and the class is not constructed.
1809///
1810/// Constructors are designated by either naming the method `__construct` or by
1811/// annotating a method with the `#[php(constructor)]` attribute. Note that when
1812/// using the attribute, the function is not exported to PHP like a regular
1813/// method.
1814///
1815/// Constructors cannot use the visibility or rename attributes listed above.
1816///
1817/// ## Constants
1818///
1819/// Constants are defined as regular Rust `impl` constants. Any type that
1820/// implements `IntoZval` can be used as a constant. Constant visibility is not
1821/// supported at the moment, and therefore no attributes are valid on constants.
1822///
1823/// ## Property getters and setters
1824///
1825/// You can add properties to classes which use Rust functions as getters and/or
1826/// setters. This is done with the `#[php(getter)]` and `#[php(setter)]`
1827/// attributes. By default, the `get_` or `set_` prefix is trimmed from the
1828/// start of the function name, and the remainder is used as the property name.
1829///
1830/// If you want to use a different name for the property, you can pass a `name`
1831/// or `change_case` option to the `#[php]` attribute which will change the
1832/// property name.
1833///
1834/// Properties do not necessarily have to have both a getter and a setter, if
1835/// the property is immutable the setter can be omitted, and vice versa for
1836/// getters.
1837///
1838/// The `#[php(getter)]` and `#[php(setter)]` attributes are mutually exclusive
1839/// on methods. Properties cannot have multiple getters or setters, and the
1840/// property name cannot conflict with field properties defined on the struct.
1841///
1842/// As the same as field properties, method property types must implement both
1843/// `IntoZval` and `FromZval`.
1844///
1845/// ### Overriding field properties with getters/setters
1846///
1847/// If you have a field property defined with `#[php(prop)]` on your struct, you
1848/// can override its access by defining a getter or setter method with the same
1849/// property name. The method-based property will take precedence:
1850///
1851/// ```rust,ignore
1852/// use ext_php_rs::prelude::*;
1853///
1854/// #[php_class]
1855/// pub struct Book {
1856///     #[php(prop)]
1857///     pub title: String,  // Direct field access
1858/// }
1859///
1860/// #[php_impl]
1861/// impl Book {
1862///     pub fn __construct(title: String) -> Self {
1863///         Self { title }
1864///     }
1865///
1866///     // This getter overrides $book->title access
1867///     #[php(getter)]
1868///     pub fn get_title(&self) -> String {
1869///         format!("Title: {}", self.title)
1870///     }
1871/// }
1872/// ```
1873///
1874/// In PHP, accessing `$book->title` will now call the `get_title()` method
1875/// instead of directly accessing the field:
1876///
1877/// ```php
1878/// $book = new Book("The Rust Book");
1879/// echo $book->title; // Output: "Title: The Rust Book"
1880/// ```
1881///
1882/// This is useful when you need to add validation, transformation, or side
1883/// effects to property access while still having the convenience of a public
1884/// field in Rust.
1885///
1886/// ## Example
1887///
1888/// Continuing on from our `Human` example in the structs section, we will
1889/// define a constructor, as well as getters for the properties. We will also
1890/// define a constant for the maximum age of a `Human`.
1891///
1892/// ```rust,no_run,ignore
1893/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1894/// # extern crate ext_php_rs;
1895/// use ext_php_rs::{prelude::*, types::ZendClassObject};
1896///
1897/// #[php_class]
1898/// #[derive(Debug, Default)]
1899/// pub struct Human {
1900///     name: String,
1901///     age: i32,
1902///     #[php(prop)]
1903///     address: String,
1904/// }
1905///
1906/// #[php_impl]
1907/// impl Human {
1908///     const MAX_AGE: i32 = 100;
1909///
1910///     // No `#[constructor]` attribute required here - the name is `__construct`.
1911///     pub fn __construct(name: String, age: i32) -> Self {
1912///         Self {
1913///             name,
1914///             age,
1915///             address: String::new()
1916///         }
1917///     }
1918///
1919///     #[php(getter)]
1920///     pub fn get_name(&self) -> String {
1921///         self.name.to_string()
1922///     }
1923///
1924///     #[php(setter)]
1925///     pub fn set_name(&mut self, name: String) {
1926///         self.name = name;
1927///     }
1928///
1929///     #[php(getter)]
1930///     pub fn get_age(&self) -> i32 {
1931///         self.age
1932///     }
1933///
1934///     pub fn introduce(&self) {
1935///         println!("My name is {} and I am {} years old. I live at {}.", self.name, self.age, self.address);
1936///     }
1937///
1938///     pub fn get_raw_obj(self_: &mut ZendClassObject<Human>) -> &mut ZendClassObject<Human> {
1939///         dbg!(self_)
1940///     }
1941///
1942///     pub fn get_max_age() -> i32 {
1943///         Self::MAX_AGE
1944///     }
1945/// }
1946/// #[php_module]
1947/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
1948///     module.class::<Human>()
1949/// }
1950/// # fn main() {}
1951/// ```
1952///
1953/// Using our newly created class in PHP:
1954///
1955/// ```php
1956/// <?php
1957///
1958/// $me = new Human('David', 20);
1959///
1960/// $me->introduce(); // My name is David and I am 20 years old.
1961/// var_dump(Human::get_max_age()); // int(100)
1962/// var_dump(Human::MAX_AGE); // int(100)
1963/// ```
1964///
1965/// [`php_async_impl`]: ./async_impl.md
1966// END DOCS FROM impl.md
1967#[proc_macro_attribute]
1968pub fn php_impl(args: TokenStream, input: TokenStream) -> TokenStream {
1969    php_impl_internal(args.into(), input.into()).into()
1970}
1971
1972#[allow(clippy::needless_pass_by_value)]
1973fn php_impl_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
1974    let input = parse_macro_input2!(input as ItemImpl);
1975    if !args.is_empty() {
1976        return err!(input => "`#[php_impl(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error();
1977    }
1978
1979    impl_::parser(input).unwrap_or_else(|e| e.to_compile_error())
1980}
1981
1982/// # `#[php_impl_interface]` Attribute
1983///
1984/// Marks a trait implementation as implementing a PHP interface. This allows
1985/// Rust structs marked with `#[php_class]` to implement Rust traits marked
1986/// with `#[php_interface]`, and have PHP recognize the relationship.
1987///
1988/// **Key feature**: The macro automatically registers the trait methods as PHP
1989/// methods on the class. You don't need to duplicate them in a separate
1990/// `#[php_impl]` block.
1991///
1992/// ## Usage
1993///
1994/// ```rust,no_run,ignore
1995/// # #![cfg_attr(windows, feature(abi_vectorcall))]
1996/// # extern crate ext_php_rs;
1997/// use ext_php_rs::prelude::*;
1998///
1999/// #[php_interface]
2000/// trait MyInterface {
2001///     fn my_method(&self) -> String;
2002/// }
2003///
2004/// #[php_class]
2005/// struct MyClass;
2006///
2007/// // The trait method my_method() is automatically registered as a PHP method
2008/// #[php_impl_interface]
2009/// impl MyInterface for MyClass {
2010///     fn my_method(&self) -> String {
2011///         "Hello from MyClass!".to_string()
2012///     }
2013/// }
2014///
2015/// #[php_module]
2016/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
2017///     module
2018///         .interface::<PhpInterfaceMyInterface>()
2019///         .class::<MyClass>()
2020/// }
2021/// # fn main() {}
2022/// ```
2023///
2024/// After registration, PHP's `is_a($obj, 'MyInterface')` will return `true`
2025/// for instances of `MyClass`, and `$obj->myMethod()` will be callable.
2026///
2027/// ## Options
2028///
2029/// ### `change_method_case`
2030///
2031/// If the interface uses a non-default `change_method_case` (e.g.,
2032/// `#[php(change_method_case = "snake_case")]`), you must specify the same
2033/// setting on `#[php_impl_interface]` to ensure method names match:
2034///
2035/// ```rust,no_run,ignore
2036/// #[php_interface]
2037/// #[php(change_method_case = "snake_case")]
2038/// trait MyInterface {
2039///     fn my_method(&self) -> String;
2040/// }
2041///
2042/// #[php_impl_interface(change_method_case = "snake_case")]
2043/// impl MyInterface for MyClass {
2044///     fn my_method(&self) -> String {
2045///         "Hello!".to_string()
2046///     }
2047/// }
2048/// ```
2049///
2050/// The default is `camelCase` (matching the interface default).
2051///
2052/// ## Requirements
2053///
2054/// - The trait must be marked with `#[php_interface]`
2055/// - The struct must be marked with `#[php_class]`
2056/// - The interface must be registered before the class in the module builder
2057#[proc_macro_attribute]
2058pub fn php_impl_interface(args: TokenStream, input: TokenStream) -> TokenStream {
2059    php_impl_interface_internal(args.into(), input.into()).into()
2060}
2061
2062#[allow(clippy::needless_pass_by_value)]
2063fn php_impl_interface_internal(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
2064    let input = parse_macro_input2!(input as ItemImpl);
2065    let attr_args = match darling::ast::NestedMeta::parse_meta_list(args) {
2066        Ok(v) => v,
2067        Err(e) => return e.to_compile_error(),
2068    };
2069    let args = match impl_interface::PhpImplInterfaceArgs::from_list(&attr_args) {
2070        Ok(v) => v,
2071        Err(e) => return e.write_errors(),
2072    };
2073
2074    impl_interface::parser(args, &input).unwrap_or_else(|e| e.to_compile_error())
2075}
2076
2077// BEGIN DOCS FROM extern.md
2078/// # `#[php_extern]` Attribute
2079///
2080/// Attribute used to annotate `extern` blocks which are deemed as PHP
2081/// functions.
2082///
2083/// This allows you to 'import' PHP functions into Rust so that they can be
2084/// called like regular Rust functions. Parameters can be any type that
2085/// implements [`IntoZval`], and the return type can be anything that implements
2086/// [`From<Zval>`] (notice how [`Zval`] is consumed rather than borrowed in this
2087/// case).
2088///
2089/// Unlike most other attributes, this does not need to be placed inside a
2090/// `#[php_module]` block.
2091///
2092/// # Panics
2093///
2094/// The function can panic when called under a few circumstances:
2095///
2096/// * The function could not be found or was not callable.
2097/// * One of the parameters could not be converted into a [`Zval`].
2098/// * The actual function call failed internally.
2099/// * The output [`Zval`] could not be parsed into the output type.
2100///
2101/// The last point can be important when interacting with functions that return
2102/// unions, such as [`strpos`] which can return an integer or a boolean. In this
2103/// case, a [`Zval`] should be returned as parsing a boolean to an integer is
2104/// invalid, and vice versa.
2105///
2106/// # Example
2107///
2108/// This `extern` block imports the [`strpos`] function from PHP. Notice that
2109/// the string parameters can take either [`String`] or [`&str`], the optional
2110/// parameter `offset` is an [`Option<i64>`], and the return value is a [`Zval`]
2111/// as the return type is an integer-boolean union.
2112///
2113/// ```rust,no_run,ignore
2114/// # #![cfg_attr(windows, feature(abi_vectorcall))]
2115/// # extern crate ext_php_rs;
2116/// use ext_php_rs::{
2117///     prelude::*,
2118///     types::Zval,
2119/// };
2120///
2121/// #[php_extern]
2122/// extern "C" {
2123///     fn strpos(haystack: &str, needle: &str, offset: Option<i64>) -> Zval;
2124/// }
2125///
2126/// #[php_function]
2127/// pub fn my_strpos() {
2128///     assert_eq!(unsafe { strpos("Hello", "e", None) }.long(), Some(1));
2129/// }
2130///
2131/// #[php_module]
2132/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
2133///     module.function(wrap_function!(my_strpos))
2134/// }
2135/// # fn main() {}
2136/// ```
2137///
2138/// [`strpos`]: https://www.php.net/manual/en/function.strpos.php
2139/// [`IntoZval`]: crate::convert::IntoZval
2140/// [`Zval`]: crate::types::Zval
2141// END DOCS FROM extern.md
2142#[proc_macro_attribute]
2143pub fn php_extern(args: TokenStream, input: TokenStream) -> TokenStream {
2144    php_extern_internal(args.into(), input.into()).into()
2145}
2146
2147#[allow(clippy::needless_pass_by_value)]
2148fn php_extern_internal(_: TokenStream2, input: TokenStream2) -> TokenStream2 {
2149    let input = parse_macro_input2!(input as ItemForeignMod);
2150
2151    extern_::parser(input).unwrap_or_else(|e| e.to_compile_error())
2152}
2153
2154// BEGIN DOCS FROM zval_convert.md
2155/// # `ZvalConvert` Derive Macro
2156///
2157/// The `#[derive(ZvalConvert)]` macro derives the `FromZval` and `IntoZval`
2158/// traits on a struct or enum.
2159///
2160/// ## Structs
2161///
2162/// When used on a struct, the `FromZendObject` and `IntoZendObject` traits are
2163/// also implemented, mapping fields to properties in both directions. All
2164/// fields on the struct must implement `FromZval` as well. Generics are allowed
2165/// on structs that use the derive macro, however, the implementation will add a
2166/// `FromZval` bound to all generics types.
2167///
2168/// ### Examples
2169///
2170/// ```rust,no_run,ignore
2171/// # #![cfg_attr(windows, feature(abi_vectorcall))]
2172/// # extern crate ext_php_rs;
2173/// use ext_php_rs::prelude::*;
2174///
2175/// #[derive(ZvalConvert)]
2176/// pub struct ExampleClass<'a> {
2177///     a: i32,
2178///     b: String,
2179///     c: &'a str
2180/// }
2181///
2182/// #[php_function]
2183/// pub fn take_object(obj: ExampleClass) {
2184///     dbg!(obj.a, obj.b, obj.c);
2185/// }
2186///
2187/// #[php_function]
2188/// pub fn give_object() -> ExampleClass<'static> {
2189///     ExampleClass {
2190///         a: 5,
2191///         b: "String".to_string(),
2192///         c: "Borrowed",
2193///     }
2194/// }
2195///
2196/// #[php_module]
2197/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
2198///     module
2199///         .function(wrap_function!(take_object))
2200///         .function(wrap_function!(give_object))
2201/// }
2202/// # fn main() {}
2203/// ```
2204///
2205/// Calling from PHP:
2206///
2207/// ```php
2208/// <?php
2209///
2210/// $obj = new stdClass;
2211/// $obj->a = 5;
2212/// $obj->b = 'Hello, world!';
2213/// $obj->c = 'another string';
2214///
2215/// take_object($obj);
2216/// var_dump(give_object());
2217/// ```
2218///
2219/// Another example involving generics:
2220///
2221/// ```rust,no_run,ignore
2222/// # #![cfg_attr(windows, feature(abi_vectorcall))]
2223/// # extern crate ext_php_rs;
2224/// use ext_php_rs::prelude::*;
2225///
2226/// // T must implement both `PartialEq<i32>` and `FromZval`.
2227/// #[derive(Debug, ZvalConvert)]
2228/// pub struct CompareVals<T: PartialEq<i32>> {
2229///     a: T,
2230///     b: T
2231/// }
2232///
2233/// #[php_function]
2234/// pub fn take_object(obj: CompareVals<i32>) {
2235///     dbg!(obj);
2236/// }
2237///
2238/// #[php_module]
2239/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
2240///     module
2241///         .function(wrap_function!(take_object))
2242/// }
2243/// # fn main() {}
2244/// ```
2245///
2246/// ## Enums
2247///
2248/// When used on an enum, the `FromZval` implementation will treat the enum as a
2249/// tagged union with a mixed datatype. This allows you to accept multiple types
2250/// in a parameter, for example, a string and an integer.
2251///
2252/// The enum variants must not have named fields, and each variant must have
2253/// exactly one field (the type to extract from the zval). Optionally, the enum
2254/// may have one default variant with no data contained, which will be used when
2255/// the rest of the variants could not be extracted from the zval.
2256///
2257/// The ordering of the variants in the enum is important, as the `FromZval`
2258/// implementation will attempt to parse the zval data in order. For example, if
2259/// you put a `String` variant before an integer variant, the integer would be
2260/// converted to a string and passed as the string variant.
2261///
2262/// ### Examples
2263///
2264/// Basic example showing the importance of variant ordering and default field:
2265///
2266/// ```rust,no_run,ignore
2267/// # #![cfg_attr(windows, feature(abi_vectorcall))]
2268/// # extern crate ext_php_rs;
2269/// use ext_php_rs::prelude::*;
2270///
2271/// #[derive(Debug, ZvalConvert)]
2272/// pub enum UnionExample<'a> {
2273///     Long(u64), // Long
2274///     ProperStr(&'a str), // Actual string - not a converted value
2275///     ParsedStr(String), // Potentially parsed string, i.e. a double
2276///     None // Zval did not contain anything that could be parsed above
2277/// }
2278///
2279/// #[php_function]
2280/// pub fn test_union(val: UnionExample) {
2281///     dbg!(val);
2282/// }
2283///
2284/// #[php_function]
2285/// pub fn give_union() -> UnionExample<'static> {
2286///     UnionExample::Long(5)
2287/// }
2288///
2289/// #[php_module]
2290/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
2291///     module
2292///         .function(wrap_function!(test_union))
2293///         .function(wrap_function!(give_union))
2294/// }
2295/// # fn main() {}
2296/// ```
2297///
2298/// Use in PHP:
2299///
2300/// ```php
2301/// test_union(5); // UnionExample::Long(5)
2302/// test_union("Hello, world!"); // UnionExample::ProperStr("Hello, world!")
2303/// test_union(5.66666); // UnionExample::ParsedStr("5.6666")
2304/// test_union(null); // UnionExample::None
2305/// var_dump(give_union()); // int(5)
2306/// ```
2307// END DOCS FROM zval_convert.md
2308#[proc_macro_derive(ZvalConvert)]
2309pub fn zval_convert_derive(input: TokenStream) -> TokenStream {
2310    zval_convert_derive_internal(input.into()).into()
2311}
2312
2313fn zval_convert_derive_internal(input: TokenStream2) -> TokenStream2 {
2314    let input = parse_macro_input2!(input as DeriveInput);
2315
2316    zval::parser(input).unwrap_or_else(|e| e.to_compile_error())
2317}
2318
2319/// Defines an `extern` function with the Zend fastcall convention based on
2320/// operating system.
2321///
2322/// On Windows, Zend fastcall functions use the vector calling convention, while
2323/// on all other operating systems no fastcall convention is used (just the
2324/// regular C calling convention).
2325///
2326/// This macro wraps a function and applies the correct calling convention.
2327///
2328/// ## Examples
2329///
2330/// ```rust,ignore
2331/// # #![cfg_attr(windows, feature(abi_vectorcall))]
2332/// use ext_php_rs::zend_fastcall;
2333///
2334/// zend_fastcall! {
2335///     pub extern fn test_hello_world(a: i32, b: i32) -> i32 {
2336///         a + b
2337///     }
2338/// }
2339/// ```
2340///
2341/// On Windows, this function will have the signature `pub extern "vectorcall"
2342/// fn(i32, i32) -> i32`, while on macOS/Linux the function will have the
2343/// signature `pub extern "C" fn(i32, i32) -> i32`.
2344///
2345/// ## Support
2346///
2347/// The `vectorcall` ABI is currently only supported on Windows with nightly
2348/// Rust and the `abi_vectorcall` feature enabled.
2349#[proc_macro]
2350pub fn zend_fastcall(input: TokenStream) -> TokenStream {
2351    zend_fastcall_internal(input.into()).into()
2352}
2353
2354fn zend_fastcall_internal(input: TokenStream2) -> TokenStream2 {
2355    let input = parse_macro_input2!(input as ItemFn);
2356
2357    fastcall::parser(input)
2358}
2359
2360/// Wraps a function to be used in the [`Module::function`] method.
2361#[proc_macro]
2362pub fn wrap_function(input: TokenStream) -> TokenStream {
2363    wrap_function_internal(input.into()).into()
2364}
2365
2366fn wrap_function_internal(input: TokenStream2) -> TokenStream2 {
2367    let input = parse_macro_input2!(input as syn::Path);
2368
2369    match function::wrap(&input) {
2370        Ok(parsed) => parsed,
2371        Err(e) => e.to_compile_error(),
2372    }
2373}
2374
2375/// Wraps a constant to be used in the [`ModuleBuilder::constant`] method.
2376#[proc_macro]
2377pub fn wrap_constant(input: TokenStream) -> TokenStream {
2378    wrap_constant_internal(input.into()).into()
2379}
2380
2381fn wrap_constant_internal(input: TokenStream2) -> TokenStream2 {
2382    let input = parse_macro_input2!(input as syn::Path);
2383
2384    match constant::wrap(&input) {
2385        Ok(parsed) => parsed,
2386        Err(e) => e.to_compile_error(),
2387    }
2388}
2389
2390macro_rules! parse_macro_input2 {
2391    ($tokenstream:ident as $ty:ty) => {
2392        match syn::parse2::<$ty>($tokenstream) {
2393            Ok(data) => data,
2394            Err(err) => {
2395                return proc_macro2::TokenStream::from(err.to_compile_error());
2396            }
2397        }
2398    };
2399    ($tokenstream:ident) => {
2400        $crate::parse_macro_input!($tokenstream as _)
2401    };
2402}
2403
2404pub(crate) use parse_macro_input2;
2405
2406macro_rules! err {
2407    ($span:expr => $($msg:tt)*) => {
2408        ::syn::Error::new(::syn::spanned::Spanned::span(&$span), format!($($msg)*))
2409    };
2410    ($($msg:tt)*) => {
2411        ::syn::Error::new(::proc_macro2::Span::call_site(), format!($($msg)*))
2412    };
2413}
2414
2415/// Bails out of a function with a syn error.
2416macro_rules! bail {
2417    ($span:expr => $($msg:tt)*) => {
2418        return Err($crate::err!($span => $($msg)*))
2419    };
2420    ($($msg:tt)*) => {
2421        return Err($crate::err!($($msg)*))
2422    };
2423}
2424
2425pub(crate) use bail;
2426pub(crate) use err;
2427
2428pub(crate) mod prelude {
2429    pub(crate) trait OptionTokens {
2430        fn option_tokens(&self) -> proc_macro2::TokenStream;
2431    }
2432
2433    impl<T: quote::ToTokens> OptionTokens for Option<T> {
2434        fn option_tokens(&self) -> proc_macro2::TokenStream {
2435            if let Some(token) = self {
2436                quote::quote! { ::std::option::Option::Some(#token) }
2437            } else {
2438                quote::quote! { ::std::option::Option::None }
2439            }
2440        }
2441    }
2442
2443    pub(crate) use crate::{bail, err};
2444    pub(crate) type Result<T> = std::result::Result<T, syn::Error>;
2445}
2446
2447#[cfg(test)]
2448mod tests {
2449    use super::*;
2450    use std::path::PathBuf;
2451
2452    type AttributeFn =
2453        fn(proc_macro2::TokenStream, proc_macro2::TokenStream) -> proc_macro2::TokenStream;
2454    type FunctionLikeFn = fn(proc_macro2::TokenStream) -> proc_macro2::TokenStream;
2455
2456    #[test]
2457    pub fn test_expand() {
2458        macrotest::expand("tests/expand/*.rs");
2459        for entry in glob::glob("tests/expand/*.rs").expect("Failed to read expand tests glob") {
2460            let entry = entry.expect("Failed to read expand test file");
2461            runtime_expand_attr(&entry);
2462            runtime_expand_func(&entry);
2463            runtime_expand_derive(&entry);
2464        }
2465    }
2466
2467    fn runtime_expand_attr(path: &PathBuf) {
2468        let file = std::fs::File::open(path).expect("Failed to open expand test file");
2469        runtime_macros::emulate_attributelike_macro_expansion(
2470            file,
2471            &[
2472                ("php_class", php_class_internal as AttributeFn),
2473                ("php_const", php_const_internal as AttributeFn),
2474                ("php_enum", php_enum_internal as AttributeFn),
2475                ("php_interface", php_interface_internal as AttributeFn),
2476                ("php_extern", php_extern_internal as AttributeFn),
2477                ("php_function", php_function_internal as AttributeFn),
2478                ("php_impl", php_impl_internal as AttributeFn),
2479                (
2480                    "php_impl_interface",
2481                    php_impl_interface_internal as AttributeFn,
2482                ),
2483                ("php_module", php_module_internal as AttributeFn),
2484            ],
2485        )
2486        .expect("Failed to expand attribute macros in test file");
2487    }
2488
2489    fn runtime_expand_func(path: &PathBuf) {
2490        let file = std::fs::File::open(path).expect("Failed to open expand test file");
2491        runtime_macros::emulate_functionlike_macro_expansion(
2492            file,
2493            &[
2494                ("zend_fastcall", zend_fastcall_internal as FunctionLikeFn),
2495                ("wrap_function", wrap_function_internal as FunctionLikeFn),
2496                ("wrap_constant", wrap_constant_internal as FunctionLikeFn),
2497            ],
2498        )
2499        .expect("Failed to expand function-like macros in test file");
2500    }
2501
2502    fn runtime_expand_derive(path: &PathBuf) {
2503        let file = std::fs::File::open(path).expect("Failed to open expand test file");
2504        runtime_macros::emulate_derive_macro_expansion(
2505            file,
2506            &[("ZvalConvert", zval_convert_derive_internal)],
2507        )
2508        .expect("Failed to expand derive macros in test file");
2509    }
2510}