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