ext_php_rs_derive/
lib.rs

1//! Macros for the `php-ext` crate.
2mod class;
3mod constant;
4mod extern_;
5mod fastcall;
6mod function;
7mod helpers;
8mod impl_;
9mod module;
10mod parsing;
11mod syn_ext;
12mod zval;
13
14use proc_macro::TokenStream;
15use syn::{
16    parse_macro_input, DeriveInput, ItemConst, ItemFn, ItemForeignMod, ItemImpl, ItemStruct,
17};
18
19extern crate proc_macro;
20
21// BEGIN DOCS FROM classes.md
22/// # `#[php_class]` Attribute
23///
24/// Structs can be exported to PHP as classes with the `#[php_class]` attribute
25/// macro. This attribute derives the `RegisteredClass` trait on your struct, as
26/// well as registering the class to be registered with the `#[php_module]`
27/// macro.
28///
29/// ## Options
30///
31/// There are additional macros that modify the class. These macros **must** be
32/// placed underneath the `#[php_class]` attribute.
33///
34/// - `name` - Changes the name of the class when exported to PHP. The Rust
35///   struct name is kept the same. If no name is given, the name of the struct
36///   is used. Useful for namespacing classes.
37/// - `change_case` - Changes the case of the class name when exported to PHP.
38/// - `#[php(extends(ce = ce_fn, stub = "ParentClass"))]` - Sets the parent
39///   class of the class. Can only be used once. `ce_fn` must be a function with
40///   the signature `fn() -> &'static ClassEntry`.
41/// - `#[php(implements(ce = ce_fn, stub = "InterfaceName"))]` - Implements the
42///   given interface on the class. Can be used multiple times. `ce_fn` must be
43///   a valid function with the signature `fn() -> &'static ClassEntry`.
44///
45/// You may also use the `#[php(prop)]` attribute on a struct field to use the
46/// field as a PHP property. By default, the field will be accessible from PHP
47/// publicly with the same name as the field. Property types must implement
48/// `IntoZval` and `FromZval`.
49///
50/// You can rename the property with options:
51///
52/// - `name` - Allows you to rename the property, e.g. `#[php(name =
53///   "new_name")]`
54/// - `change_case` - Allows you to rename the property using rename rules, e.g.
55///   `#[php(change_case = PascalCase)]`
56///
57/// ## Restrictions
58///
59/// ### No lifetime parameters
60///
61/// Rust lifetimes are used by the Rust compiler to reason about a program's
62/// memory safety. They are a compile-time only concept;
63/// there is no way to access Rust lifetimes at runtime from a dynamic language
64/// like PHP.
65///
66/// As soon as Rust data is exposed to PHP,
67/// there is no guarantee which the Rust compiler can make on how long the data
68/// will live. PHP is a reference-counted language and those references can be
69/// held for an arbitrarily long time, which is untraceable by the Rust
70/// compiler. The only possible way to express this correctly is to require that
71/// any `#[php_class]` does not borrow data for any lifetime shorter than the
72/// `'static` lifetime, i.e. the `#[php_class]` cannot have any lifetime
73/// parameters.
74///
75/// When you need to share ownership of data between PHP and Rust,
76/// instead of using borrowed references with lifetimes, consider using
77/// reference-counted smart pointers such as [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html).
78///
79/// ### No generic parameters
80///
81/// A Rust struct `Foo<T>` with a generic parameter `T` generates new compiled
82/// implementations each time it is used with a different concrete type for `T`.
83/// These new implementations are generated by the compiler at each usage site.
84/// This is incompatible with wrapping `Foo` in PHP,
85/// where there needs to be a single compiled implementation of `Foo` which is
86/// integrated with the PHP interpreter.
87///
88/// ## Example
89///
90/// This example creates a PHP class `Human`, adding a PHP property `address`.
91///
92/// ```rust,no_run,ignore
93/// # #![cfg_attr(windows, feature(abi_vectorcall))]
94/// # extern crate ext_php_rs;
95/// use ext_php_rs::prelude::*;
96///
97/// #[php_class]
98/// pub struct Human {
99///     name: String,
100///     age: i32,
101///     #[php(prop)]
102///     address: String,
103/// }
104///
105/// #[php_module]
106/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
107///     module.class::<Human>()
108/// }
109/// # fn main() {}
110/// ```
111///
112/// Create a custom exception `RedisException`, which extends `Exception`, and
113/// put it in the `Redis\Exception` namespace:
114///
115/// ```rust,no_run,ignore
116/// # #![cfg_attr(windows, feature(abi_vectorcall))]
117/// # extern crate ext_php_rs;
118/// use ext_php_rs::{
119///     prelude::*,
120///     exception::PhpException,
121///     zend::ce
122/// };
123///
124/// #[php_class]
125/// #[php(name = "Redis\\Exception\\RedisException")]
126/// #[php(extends(ce = ce::exception, stub = "\\Exception"))]
127/// #[derive(Default)]
128/// pub struct RedisException;
129///
130/// // Throw our newly created exception
131/// #[php_function]
132/// pub fn throw_exception() -> PhpResult<i32> {
133///     Err(PhpException::from_class::<RedisException>("Not good!".into()))
134/// }
135///
136/// #[php_module]
137/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
138///     module
139///         .class::<RedisException>()
140///         .function(wrap_function!(throw_exception))
141/// }
142/// # fn main() {}
143/// ```
144///
145/// ## Implementing an Interface
146///
147/// To implement an interface, use `#[php(implements(ce = ce_fn, stub =
148/// "InterfaceName")]` where `ce_fn` is an function returning a `ClassEntry`. The following example implements [`ArrayAccess`](https://www.php.net/manual/en/class.arrayaccess.php):
149///
150/// ````rust,no_run,ignore
151/// # #![cfg_attr(windows, feature(abi_vectorcall))]
152/// # extern crate ext_php_rs;
153/// use ext_php_rs::{
154///     prelude::*,
155///     exception::PhpResult,
156///     types::Zval,
157///     zend::ce,
158/// };
159///
160/// #[php_class]
161/// #[php(implements(ce = ce::arrayaccess, stub = "\\ArrayAccess"))]
162/// #[derive(Default)]
163/// pub struct EvenNumbersArray;
164///
165/// /// Returns `true` if the array offset is an even number.
166/// /// Usage:
167/// /// ```php
168/// /// $arr = new EvenNumbersArray();
169/// /// var_dump($arr[0]); // true
170/// /// var_dump($arr[1]); // false
171/// /// var_dump($arr[2]); // true
172/// /// var_dump($arr[3]); // false
173/// /// var_dump($arr[4]); // true
174/// /// var_dump($arr[5] = true); // Fatal error:  Uncaught Exception: Setting values is not supported
175/// /// ```
176/// #[php_impl]
177/// impl EvenNumbersArray {
178///     pub fn __construct() -> EvenNumbersArray {
179///         EvenNumbersArray {}
180///     }
181///     // We need to use `Zval` because ArrayAccess needs $offset to be a `mixed`
182///     pub fn offset_exists(&self, offset: &'_ Zval) -> bool {
183///         offset.is_long()
184///     }
185///     pub fn offset_get(&self, offset: &'_ Zval) -> PhpResult<bool> {
186///         let integer_offset = offset.long().ok_or("Expected integer offset")?;
187///         Ok(integer_offset % 2 == 0)
188///     }
189///     pub fn offset_set(&mut self, _offset: &'_ Zval, _value: &'_ Zval) -> PhpResult {
190///         Err("Setting values is not supported".into())
191///     }
192///     pub fn offset_unset(&mut self, _offset: &'_ Zval) -> PhpResult {
193///         Err("Setting values is not supported".into())
194///     }
195/// }
196///
197/// #[php_module]
198/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
199///     module.class::<EvenNumbersArray>()
200/// }
201/// # fn main() {}
202/// ````
203// END DOCS FROM classes.md
204#[proc_macro_attribute]
205pub fn php_class(args: TokenStream, input: TokenStream) -> TokenStream {
206    let input = parse_macro_input!(input as ItemStruct);
207    if !args.is_empty() {
208        return err!(input => "`#[php_class(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error().into();
209    }
210
211    class::parser(input)
212        .unwrap_or_else(|e| e.to_compile_error())
213        .into()
214}
215
216// BEGIN DOCS FROM function.md
217/// # `#[php_function]` Attribute
218///
219/// Used to annotate functions which should be exported to PHP. Note that this
220/// should not be used on class methods - see the `#[php_impl]` macro for that.
221///
222/// See the [list of types](../types/index.md) that are valid as parameter and
223/// return types.
224///
225/// ## Optional parameters
226///
227/// Optional parameters can be used by setting the Rust parameter type to a
228/// variant of `Option<T>`. The macro will then figure out which parameters are
229/// optional by using the last consecutive arguments that are a variant of
230/// `Option<T>` or have a default value.
231///
232/// ```rust,no_run,ignore
233/// # #![cfg_attr(windows, feature(abi_vectorcall))]
234/// # extern crate ext_php_rs;
235/// use ext_php_rs::prelude::*;
236///
237/// #[php_function]
238/// pub fn greet(name: String, age: Option<i32>) -> String {
239///     let mut greeting = format!("Hello, {}!", name);
240///
241///     if let Some(age) = age {
242///         greeting += &format!(" You are {} years old.", age);
243///     }
244///
245///     greeting
246/// }
247///
248/// #[php_module]
249/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
250///     module.function(wrap_function!(greet))
251/// }
252/// # fn main() {}
253/// ```
254///
255/// Default parameter values can also be set for optional parameters. This is
256/// done through the `#[php(defaults)]` attribute option. When an optional
257/// parameter has a default, it does not need to be a variant of `Option`:
258///
259/// ```rust,no_run,ignore
260/// # #![cfg_attr(windows, feature(abi_vectorcall))]
261/// # extern crate ext_php_rs;
262/// use ext_php_rs::prelude::*;
263///
264/// #[php_function]
265/// #[php(defaults(offset = 0))]
266/// pub fn rusty_strpos(haystack: &str, needle: &str, offset: i64) -> Option<usize> {
267///     let haystack: String = haystack.chars().skip(offset as usize).collect();
268///     haystack.find(needle)
269/// }
270///
271/// #[php_module]
272/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
273///     module.function(wrap_function!(rusty_strpos))
274/// }
275/// # fn main() {}
276/// ```
277///
278/// Note that if there is a non-optional argument after an argument that is a
279/// variant of `Option<T>`, the `Option<T>` argument will be deemed a nullable
280/// argument rather than an optional argument.
281///
282/// ```rust,no_run,ignore
283/// # #![cfg_attr(windows, feature(abi_vectorcall))]
284/// # extern crate ext_php_rs;
285/// use ext_php_rs::prelude::*;
286///
287/// /// `age` will be deemed required and nullable rather than optional.
288/// #[php_function]
289/// pub fn greet(name: String, age: Option<i32>, description: String) -> String {
290///     let mut greeting = format!("Hello, {}!", name);
291///
292///     if let Some(age) = age {
293///         greeting += &format!(" You are {} years old.", age);
294///     }
295///
296///     greeting += &format!(" {}.", description);
297///     greeting
298/// }
299///
300/// #[php_module]
301/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
302///     module.function(wrap_function!(greet))
303/// }
304/// # fn main() {}
305/// ```
306///
307/// You can also specify the optional arguments if you want to have nullable
308/// arguments before optional arguments. This is done through an attribute
309/// parameter:
310///
311/// ```rust,no_run,ignore
312/// # #![cfg_attr(windows, feature(abi_vectorcall))]
313/// # extern crate ext_php_rs;
314/// use ext_php_rs::prelude::*;
315///
316/// /// `age` will be deemed required and nullable rather than optional,
317/// /// while description will be optional.
318/// #[php_function]
319/// #[php(optional = "description")]
320/// pub fn greet(name: String, age: Option<i32>, description: Option<String>) -> String {
321///     let mut greeting = format!("Hello, {}!", name);
322///
323///     if let Some(age) = age {
324///         greeting += &format!(" You are {} years old.", age);
325///     }
326///
327///     if let Some(description) = description {
328///         greeting += &format!(" {}.", description);
329///     }
330///
331///     greeting
332/// }
333///
334/// #[php_module]
335/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
336///     module.function(wrap_function!(greet))
337/// }
338/// # fn main() {}
339/// ```
340///
341/// ## Variadic Functions
342///
343/// Variadic functions can be implemented by specifying the last argument in the
344/// Rust function to the type `&[&Zval]`. This is the equivalent of a PHP
345/// function using the `...$args` syntax.
346///
347/// ```rust,no_run,ignore
348/// # #![cfg_attr(windows, feature(abi_vectorcall))]
349/// # extern crate ext_php_rs;
350/// use ext_php_rs::{prelude::*, types::Zval};
351///
352/// /// This can be called from PHP as `add(1, 2, 3, 4, 5)`
353/// #[php_function]
354/// pub fn add(number: u32, numbers:&[&Zval]) -> u32 {
355///     // numbers is a slice of 4 Zvals all of type long
356///     number
357/// }
358///
359/// #[php_module]
360/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
361///     module.function(wrap_function!(add))
362/// }
363/// # fn main() {}
364/// ```
365///
366/// ## Returning `Result<T, E>`
367///
368/// You can also return a `Result` from the function. The error variant will be
369/// translated into an exception and thrown. See the section on
370/// [exceptions](../exceptions.md) for more details.
371// END DOCS FROM function.md
372#[proc_macro_attribute]
373pub fn php_function(args: TokenStream, input: TokenStream) -> TokenStream {
374    let input = parse_macro_input!(input as ItemFn);
375    if !args.is_empty() {
376        return err!(input => "`#[php_function(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error().into();
377    }
378
379    function::parser(input)
380        .unwrap_or_else(|e| e.to_compile_error())
381        .into()
382}
383
384// BEGIN DOCS FROM constant.md
385/// # `#[php_const]` Attribute
386///
387/// Exports a Rust constant as a global PHP constant. The constant can be any
388/// type that implements `IntoConst`.
389///
390/// The `wrap_constant!()` macro can be used to simplify the registration of
391/// constants. It sets the name and doc comments for the constant.
392///
393/// You can rename the const with options:
394///
395/// - `name` - Allows you to rename the property, e.g. `#[php(name =
396///   "new_name")]`
397/// - `change_case` - Allows you to rename the property using rename rules, e.g.
398///   `#[php(change_case = PascalCase)]`
399///
400/// ## Examples
401///
402/// ```rust,no_run,ignore
403/// # #![cfg_attr(windows, feature(abi_vectorcall))]
404/// # extern crate ext_php_rs;
405/// use ext_php_rs::prelude::*;
406///
407/// #[php_const]
408/// const TEST_CONSTANT: i32 = 100;
409///
410/// #[php_const]
411/// #[php(name = "I_AM_RENAMED")]
412/// const TEST_CONSTANT_THE_SECOND: i32 = 42;
413///
414/// #[php_const]
415/// const ANOTHER_STRING_CONST: &'static str = "Hello world!";
416///
417/// #[php_module]
418/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
419///     module
420///         .constant(wrap_constant!(TEST_CONSTANT))
421///         .constant(wrap_constant!(TEST_CONSTANT_THE_SECOND))
422///         .constant(("MANUAL_CONSTANT", ANOTHER_STRING_CONST, &[]))
423/// }
424/// # fn main() {}
425/// ```
426///
427/// ## PHP usage
428///
429/// ```php
430/// <?php
431///
432/// var_dump(TEST_CONSTANT); // int(100)
433/// var_dump(I_AM_RENAMED); // int(42)
434/// var_dump(MANUAL_CONSTANT); // string(12) "Hello world!"
435/// ```
436// END DOCS FROM constant.md
437#[proc_macro_attribute]
438pub fn php_const(args: TokenStream, input: TokenStream) -> TokenStream {
439    let input = parse_macro_input!(input as ItemConst);
440    if !args.is_empty() {
441        return err!(input => "`#[php_const(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error().into();
442    }
443
444    constant::parser(input)
445        .unwrap_or_else(|e| e.to_compile_error())
446        .into()
447}
448
449// BEGIN DOCS FROM module.md
450/// # `#[php_module]` Attribute
451///
452/// The module macro is used to annotate the `get_module` function, which is
453/// used by the PHP interpreter to retrieve information about your extension,
454/// including the name, version, functions and extra initialization functions.
455/// Regardless if you use this macro, your extension requires a `extern "C" fn
456/// get_module()` so that PHP can get this information.
457///
458/// The function is renamed to `get_module` if you have used another name. The
459/// function is passed an instance of `ModuleBuilder` which allows you to
460/// register the following (if required):
461///
462/// - Functions, classes, and constants
463/// - Extension and request startup and shutdown functions.
464///   - Read more about the PHP extension lifecycle [here](https://www.phpinternalsbook.com/php7/extensions_design/php_lifecycle.html).
465/// - PHP extension information function
466///   - Used by the `phpinfo()` function to get information about your
467///     extension.
468///
469/// Classes and constants are not registered with PHP in the `get_module`
470/// function. These are registered inside the extension startup function.
471///
472/// ## Usage
473///
474/// ```rust,no_run,ignore
475/// # #![cfg_attr(windows, feature(abi_vectorcall))]
476/// # extern crate ext_php_rs;
477/// use ext_php_rs::{
478///     prelude::*,
479///     zend::ModuleEntry,
480///     info_table_start,
481///     info_table_row,
482///     info_table_end
483/// };
484///
485/// #[php_const]
486/// pub const MY_CUSTOM_CONST: &'static str = "Hello, world!";
487///
488/// #[php_class]
489/// pub struct Test {
490///     a: i32,
491///     b: i32
492/// }
493/// #[php_function]
494/// pub fn hello_world() -> &'static str {
495///     "Hello, world!"
496/// }
497///
498/// /// Used by the `phpinfo()` function and when you run `php -i`.
499/// /// This will probably be simplified with another macro eventually!
500/// pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
501///     info_table_start!();
502///     info_table_row!("my extension", "enabled");
503///     info_table_end!();
504/// }
505///
506/// #[php_module]
507/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
508///     module
509///         .constant(wrap_constant!(MY_CUSTOM_CONST))
510///         .class::<Test>()
511///         .function(wrap_function!(hello_world))
512///         .info_function(php_module_info)
513/// }
514/// # fn main() {}
515/// ```
516// END DOCS FROM module.md
517#[proc_macro_attribute]
518pub fn php_module(args: TokenStream, input: TokenStream) -> TokenStream {
519    let input = parse_macro_input!(input as ItemFn);
520    if !args.is_empty() {
521        return err!(input => "`#[php_module(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error().into();
522    }
523
524    module::parser(input)
525        .unwrap_or_else(|e| e.to_compile_error())
526        .into()
527}
528
529// BEGIN DOCS FROM impl.md
530/// # `#[php_impl]` Attribute
531///
532/// You can export an entire `impl` block to PHP. This exports all methods as
533/// well as constants to PHP on the class that it is implemented on. This
534/// requires the `#[php_class]` macro to already be used on the underlying
535/// struct. Trait implementations cannot be exported to PHP. Only one `impl`
536/// block can be exported per class.
537///
538/// If you do not want a function exported to PHP, you should place it in a
539/// separate `impl` block.
540///
541/// If you want to use async Rust, use `#[php_async_impl]`, instead: see [here
542/// &raquo;](./async_impl.md) for more info.
543///
544/// ## Options
545///
546/// By default all constants are renamed to `UPPER_CASE` and all methods are
547/// renamed to camelCase. This can be changed by passing the
548/// `change_method_case` and `change_constant_case` as `#[php]` attributes on
549/// the `impl` block. The options are:
550///
551/// - `#[php(change_method_case = "snake_case")]` - Renames the method to snake
552///   case.
553/// - `#[php(change_constant_case = "snake_case")]` - Renames the constant to
554///   snake case.
555///
556/// See the [`name` and `change_case`](./php.md#name-and-change_case) section
557/// for a list of all available cases.
558///
559/// ## Methods
560///
561/// Methods basically follow the same rules as functions, so read about the
562/// [`php_function`] macro first. The primary difference between functions and
563/// methods is they are bounded by their class object.
564///
565/// Class methods can take a `&self` or `&mut self` parameter. They cannot take
566/// a consuming `self` parameter. Static methods can omit this `self` parameter.
567///
568/// To access the underlying Zend object, you can take a reference to a
569/// `ZendClassObject<T>` in place of the self parameter, where the parameter
570/// must be named `self_`. This can also be used to return a reference to
571/// `$this`.
572///
573/// The rest of the options are passed as separate attributes:
574///
575/// - `#[php(defaults(i = 5, b = "hello"))]` - Sets the default value for
576///   parameter(s).
577/// - `#[php(optional = i)]` - Sets the first optional parameter. Note that this
578///   also sets the remaining parameters as optional, so all optional parameters
579///   must be a variant of `Option<T>`.
580/// - `#[php(public)]`, `#[php(protected)]` and `#[php(private)]` - Sets the
581///   visibility of the method.
582/// - `#[php(name = "method_name")]` - Renames the PHP method to a different
583///   identifier, without renaming the Rust method name.
584///
585/// The `#[php(defaults)]` and `#[php(optional)]` attributes operate the same as
586/// the equivalent function attribute parameters.
587///
588/// ### Constructors
589///
590/// By default, if a class does not have a constructor, it is not constructable
591/// from PHP. It can only be returned from a Rust function to PHP.
592///
593/// Constructors are Rust methods which can take any amount of parameters and
594/// returns either `Self` or `Result<Self, E>`, where `E: Into<PhpException>`.
595/// When the error variant of `Result` is encountered, it is thrown as an
596/// exception and the class is not constructed.
597///
598/// Constructors are designated by either naming the method `__construct` or by
599/// annotating a method with the `#[php(constructor)]` attribute. Note that when
600/// using the attribute, the function is not exported to PHP like a regular
601/// method.
602///
603/// Constructors cannot use the visibility or rename attributes listed above.
604///
605/// ## Constants
606///
607/// Constants are defined as regular Rust `impl` constants. Any type that
608/// implements `IntoZval` can be used as a constant. Constant visibility is not
609/// supported at the moment, and therefore no attributes are valid on constants.
610///
611/// ## Property getters and setters
612///
613/// You can add properties to classes which use Rust functions as getters and/or
614/// setters. This is done with the `#[php(getter)]` and `#[php(setter)]`
615/// attributes. By default, the `get_` or `set_` prefix is trimmed from the
616/// start of the function name, and the remainder is used as the property name.
617///
618/// If you want to use a different name for the property, you can pass a `name`
619/// or `change_case` option to the `#[php]` attribute which will change the
620/// property name.
621///
622/// Properties do not necessarily have to have both a getter and a setter, if
623/// the property is immutable the setter can be omitted, and vice versa for
624/// getters.
625///
626/// The `#[php(getter)]` and `#[php(setter)]` attributes are mutually exclusive
627/// on methods. Properties cannot have multiple getters or setters, and the
628/// property name cannot conflict with field properties defined on the struct.
629///
630/// As the same as field properties, method property types must implement both
631/// `IntoZval` and `FromZval`.
632///
633/// ## Example
634///
635/// Continuing on from our `Human` example in the structs section, we will
636/// define a constructor, as well as getters for the properties. We will also
637/// define a constant for the maximum age of a `Human`.
638///
639/// ```rust,no_run,ignore
640/// # #![cfg_attr(windows, feature(abi_vectorcall))]
641/// # extern crate ext_php_rs;
642/// use ext_php_rs::{prelude::*, types::ZendClassObject};
643///
644/// #[php_class]
645/// #[derive(Debug, Default)]
646/// pub struct Human {
647///     name: String,
648///     age: i32,
649///     #[php(prop)]
650///     address: String,
651/// }
652///
653/// #[php_impl]
654/// impl Human {
655///     const MAX_AGE: i32 = 100;
656///
657///     // No `#[constructor]` attribute required here - the name is `__construct`.
658///     pub fn __construct(name: String, age: i32) -> Self {
659///         Self {
660///             name,
661///             age,
662///             address: String::new()
663///         }
664///     }
665///
666///     #[php(getter)]
667///     pub fn get_name(&self) -> String {
668///         self.name.to_string()
669///     }
670///
671///     #[php(setter)]
672///     pub fn set_name(&mut self, name: String) {
673///         self.name = name;
674///     }
675///
676///     #[php(getter)]
677///     pub fn get_age(&self) -> i32 {
678///         self.age
679///     }
680///
681///     pub fn introduce(&self) {
682///         println!("My name is {} and I am {} years old. I live at {}.", self.name, self.age, self.address);
683///     }
684///
685///     pub fn get_raw_obj(self_: &mut ZendClassObject<Human>) -> &mut ZendClassObject<Human> {
686///         dbg!(self_)
687///     }
688///
689///     pub fn get_max_age() -> i32 {
690///         Self::MAX_AGE
691///     }
692/// }
693/// #[php_module]
694/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
695///     module.class::<Human>()
696/// }
697/// # fn main() {}
698/// ```
699///
700/// Using our newly created class in PHP:
701///
702/// ```php
703/// <?php
704///
705/// $me = new Human('David', 20);
706///
707/// $me->introduce(); // My name is David and I am 20 years old.
708/// var_dump(Human::get_max_age()); // int(100)
709/// var_dump(Human::MAX_AGE); // int(100)
710/// ```
711///
712/// [`php_async_impl`]: ./async_impl.md
713// END DOCS FROM impl.md
714#[proc_macro_attribute]
715pub fn php_impl(args: TokenStream, input: TokenStream) -> TokenStream {
716    let input = parse_macro_input!(input as ItemImpl);
717    if !args.is_empty() {
718        return err!(input => "`#[php_impl(<args>)]` args are no longer supported. Please use `#[php(<args>)]` instead.").to_compile_error().into();
719    }
720
721    impl_::parser(input)
722        .unwrap_or_else(|e| e.to_compile_error())
723        .into()
724}
725
726// BEGIN DOCS FROM extern.md
727/// # `#[php_extern]` Attribute
728///
729/// Attribute used to annotate `extern` blocks which are deemed as PHP
730/// functions.
731///
732/// This allows you to 'import' PHP functions into Rust so that they can be
733/// called like regular Rust functions. Parameters can be any type that
734/// implements [`IntoZval`], and the return type can be anything that implements
735/// [`From<Zval>`] (notice how [`Zval`] is consumed rather than borrowed in this
736/// case).
737///
738/// Unlike most other attributes, this does not need to be placed inside a
739/// `#[php_module]` block.
740///
741/// # Panics
742///
743/// The function can panic when called under a few circumstances:
744///
745/// * The function could not be found or was not callable.
746/// * One of the parameters could not be converted into a [`Zval`].
747/// * The actual function call failed internally.
748/// * The output [`Zval`] could not be parsed into the output type.
749///
750/// The last point can be important when interacting with functions that return
751/// unions, such as [`strpos`] which can return an integer or a boolean. In this
752/// case, a [`Zval`] should be returned as parsing a boolean to an integer is
753/// invalid, and vice versa.
754///
755/// # Example
756///
757/// This `extern` block imports the [`strpos`] function from PHP. Notice that
758/// the string parameters can take either [`String`] or [`&str`], the optional
759/// parameter `offset` is an [`Option<i64>`], and the return value is a [`Zval`]
760/// as the return type is an integer-boolean union.
761///
762/// ```rust,no_run,ignore
763/// # #![cfg_attr(windows, feature(abi_vectorcall))]
764/// # extern crate ext_php_rs;
765/// use ext_php_rs::{
766///     prelude::*,
767///     types::Zval,
768/// };
769///
770/// #[php_extern]
771/// extern "C" {
772///     fn strpos(haystack: &str, needle: &str, offset: Option<i64>) -> Zval;
773/// }
774///
775/// #[php_function]
776/// pub fn my_strpos() {
777///     assert_eq!(unsafe { strpos("Hello", "e", None) }.long(), Some(1));
778/// }
779///
780/// #[php_module]
781/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
782///     module.function(wrap_function!(my_strpos))
783/// }
784/// # fn main() {}
785/// ```
786///
787/// [`strpos`]: https://www.php.net/manual/en/function.strpos.php
788/// [`IntoZval`]: crate::convert::IntoZval
789/// [`Zval`]: crate::types::Zval
790// END DOCS FROM extern.md
791#[proc_macro_attribute]
792pub fn php_extern(_: TokenStream, input: TokenStream) -> TokenStream {
793    let input = parse_macro_input!(input as ItemForeignMod);
794
795    extern_::parser(input)
796        .unwrap_or_else(|e| e.to_compile_error())
797        .into()
798}
799
800// BEGIN DOCS FROM zval_convert.md
801/// # `ZvalConvert` Derive Macro
802///
803/// The `#[derive(ZvalConvert)]` macro derives the `FromZval` and `IntoZval`
804/// traits on a struct or enum.
805///
806/// ## Structs
807///
808/// When used on a struct, the `FromZendObject` and `IntoZendObject` traits are
809/// also implemented, mapping fields to properties in both directions. All
810/// fields on the struct must implement `FromZval` as well. Generics are allowed
811/// on structs that use the derive macro, however, the implementation will add a
812/// `FromZval` bound to all generics types.
813///
814/// ### Examples
815///
816/// ```rust,no_run,ignore
817/// # #![cfg_attr(windows, feature(abi_vectorcall))]
818/// # extern crate ext_php_rs;
819/// use ext_php_rs::prelude::*;
820///
821/// #[derive(ZvalConvert)]
822/// pub struct ExampleClass<'a> {
823///     a: i32,
824///     b: String,
825///     c: &'a str
826/// }
827///
828/// #[php_function]
829/// pub fn take_object(obj: ExampleClass) {
830///     dbg!(obj.a, obj.b, obj.c);
831/// }
832///
833/// #[php_function]
834/// pub fn give_object() -> ExampleClass<'static> {
835///     ExampleClass {
836///         a: 5,
837///         b: "String".to_string(),
838///         c: "Borrowed",
839///     }
840/// }
841///
842/// #[php_module]
843/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
844///     module
845///         .function(wrap_function!(take_object))
846///         .function(wrap_function!(give_object))
847/// }
848/// # fn main() {}
849/// ```
850///
851/// Calling from PHP:
852///
853/// ```php
854/// <?php
855///
856/// $obj = new stdClass;
857/// $obj->a = 5;
858/// $obj->b = 'Hello, world!';
859/// $obj->c = 'another string';
860///
861/// take_object($obj);
862/// var_dump(give_object());
863/// ```
864///
865/// Another example involving generics:
866///
867/// ```rust,no_run,ignore
868/// # #![cfg_attr(windows, feature(abi_vectorcall))]
869/// # extern crate ext_php_rs;
870/// use ext_php_rs::prelude::*;
871///
872/// // T must implement both `PartialEq<i32>` and `FromZval`.
873/// #[derive(Debug, ZvalConvert)]
874/// pub struct CompareVals<T: PartialEq<i32>> {
875///     a: T,
876///     b: T
877/// }
878///
879/// #[php_function]
880/// pub fn take_object(obj: CompareVals<i32>) {
881///     dbg!(obj);
882/// }
883///
884/// #[php_module]
885/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
886///     module
887///         .function(wrap_function!(take_object))
888/// }
889/// # fn main() {}
890/// ```
891///
892/// ## Enums
893///
894/// When used on an enum, the `FromZval` implementation will treat the enum as a
895/// tagged union with a mixed datatype. This allows you to accept multiple types
896/// in a parameter, for example, a string and an integer.
897///
898/// The enum variants must not have named fields, and each variant must have
899/// exactly one field (the type to extract from the zval). Optionally, the enum
900/// may have one default variant with no data contained, which will be used when
901/// the rest of the variants could not be extracted from the zval.
902///
903/// The ordering of the variants in the enum is important, as the `FromZval`
904/// implementation will attempt to parse the zval data in order. For example, if
905/// you put a `String` variant before an integer variant, the integer would be
906/// converted to a string and passed as the string variant.
907///
908/// ### Examples
909///
910/// Basic example showing the importance of variant ordering and default field:
911///
912/// ```rust,no_run,ignore
913/// # #![cfg_attr(windows, feature(abi_vectorcall))]
914/// # extern crate ext_php_rs;
915/// use ext_php_rs::prelude::*;
916///
917/// #[derive(Debug, ZvalConvert)]
918/// pub enum UnionExample<'a> {
919///     Long(u64), // Long
920///     ProperStr(&'a str), // Actual string - not a converted value
921///     ParsedStr(String), // Potentially parsed string, i.e. a double
922///     None // Zval did not contain anything that could be parsed above
923/// }
924///
925/// #[php_function]
926/// pub fn test_union(val: UnionExample) {
927///     dbg!(val);
928/// }
929///
930/// #[php_function]
931/// pub fn give_union() -> UnionExample<'static> {
932///     UnionExample::Long(5)
933/// }
934///
935/// #[php_module]
936/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
937///     module
938///         .function(wrap_function!(test_union))
939///         .function(wrap_function!(give_union))
940/// }
941/// # fn main() {}
942/// ```
943///
944/// Use in PHP:
945///
946/// ```php
947/// test_union(5); // UnionExample::Long(5)
948/// test_union("Hello, world!"); // UnionExample::ProperStr("Hello, world!")
949/// test_union(5.66666); // UnionExample::ParsedStr("5.6666")
950/// test_union(null); // UnionExample::None
951/// var_dump(give_union()); // int(5)
952/// ```
953// END DOCS FROM zval_convert.md
954#[proc_macro_derive(ZvalConvert)]
955pub fn zval_convert_derive(input: TokenStream) -> TokenStream {
956    let input = parse_macro_input!(input as DeriveInput);
957
958    zval::parser(input)
959        .unwrap_or_else(|e| e.to_compile_error())
960        .into()
961}
962
963/// Defines an `extern` function with the Zend fastcall convention based on
964/// operating system.
965///
966/// On Windows, Zend fastcall functions use the vector calling convention, while
967/// on all other operating systems no fastcall convention is used (just the
968/// regular C calling convention).
969///
970/// This macro wraps a function and applies the correct calling convention.
971///
972/// ## Examples
973///
974/// ```rust,ignore
975/// # #![cfg_attr(windows, feature(abi_vectorcall))]
976/// use ext_php_rs::zend_fastcall;
977///
978/// zend_fastcall! {
979///     pub extern fn test_hello_world(a: i32, b: i32) -> i32 {
980///         a + b
981///     }
982/// }
983/// ```
984///
985/// On Windows, this function will have the signature `pub extern "vectorcall"
986/// fn(i32, i32) -> i32`, while on macOS/Linux the function will have the
987/// signature `pub extern "C" fn(i32, i32) -> i32`.
988///
989/// ## Support
990///
991/// The `vectorcall` ABI is currently only supported on Windows with nightly
992/// Rust and the `abi_vectorcall` feature enabled.
993#[proc_macro]
994pub fn zend_fastcall(input: TokenStream) -> TokenStream {
995    let input = parse_macro_input!(input as ItemFn);
996
997    fastcall::parser(input).into()
998}
999
1000/// Wraps a function to be used in the [`Module::function`] method.
1001#[proc_macro]
1002pub fn wrap_function(input: TokenStream) -> TokenStream {
1003    let input = parse_macro_input!(input as syn::Path);
1004
1005    match function::wrap(&input) {
1006        Ok(parsed) => parsed,
1007        Err(e) => e.to_compile_error(),
1008    }
1009    .into()
1010}
1011
1012/// Wraps a constant to be used in the [`ModuleBuilder::constant`] method.
1013#[proc_macro]
1014pub fn wrap_constant(input: TokenStream) -> TokenStream {
1015    let input = parse_macro_input!(input as syn::Path);
1016
1017    match constant::wrap(&input) {
1018        Ok(parsed) => parsed,
1019        Err(e) => e.to_compile_error(),
1020    }
1021    .into()
1022}
1023
1024macro_rules! err {
1025    ($span:expr => $($msg:tt)*) => {
1026        ::syn::Error::new(::syn::spanned::Spanned::span(&$span), format!($($msg)*))
1027    };
1028    ($($msg:tt)*) => {
1029        ::syn::Error::new(::proc_macro2::Span::call_site(), format!($($msg)*))
1030    };
1031}
1032
1033/// Bails out of a function with a syn error.
1034macro_rules! bail {
1035    ($span:expr => $($msg:tt)*) => {
1036        return Err($crate::err!($span => $($msg)*))
1037    };
1038    ($($msg:tt)*) => {
1039        return Err($crate::err!($($msg)*))
1040    };
1041}
1042
1043pub(crate) use bail;
1044pub(crate) use err;
1045
1046pub(crate) mod prelude {
1047    pub(crate) trait OptionTokens {
1048        fn option_tokens(&self) -> proc_macro2::TokenStream;
1049    }
1050
1051    impl<T: quote::ToTokens> OptionTokens for Option<T> {
1052        fn option_tokens(&self) -> proc_macro2::TokenStream {
1053            if let Some(token) = self {
1054                quote::quote! { ::std::option::Option::Some(#token) }
1055            } else {
1056                quote::quote! { ::std::option::Option::None }
1057            }
1058        }
1059    }
1060
1061    pub(crate) use crate::{bail, err};
1062    pub(crate) type Result<T> = std::result::Result<T, syn::Error>;
1063}