Skip to content

Commit 108444e

Browse files
committed
feat: Add doc for interface macros and add test for expand
1 parent 112d71d commit 108444e

File tree

5 files changed

+312
-2
lines changed

5 files changed

+312
-2
lines changed

crates/macros/src/interface.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub fn parser(mut input: ItemTrait) -> Result<TokenStream> {
7171
})
7272
.map(|c| {
7373
if c.default.is_none() {
74-
bail!("Interface const canot be empty");
74+
bail!("Interface const cannot be empty");
7575
}
7676
Ok(c)
7777
})

crates/macros/src/lib.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,46 @@ fn php_enum_internal(_args: TokenStream2, input: TokenStream2) -> TokenStream2 {
340340
enum_::parser(input).unwrap_or_else(|e| e.to_compile_error())
341341
}
342342

343+
// BEGIN DOCS FROM interface.md
344+
/// # `#[php_interface]` Attribute
345+
///
346+
/// Traits can be exported to PHP as interface with the `#[php_interface]` attribute
347+
/// macro. This attribute generate empty struct and derives the `RegisteredClass`.
348+
/// To register the interface use the `interface::<PhpInterface{TraitName}>()` method
349+
/// on the `ModuleBuilder` in the `#[php_module]` macro.
350+
///
351+
/// ## Options
352+
///
353+
/// The `#[php_interface]` attribute can be configured with the following options:
354+
/// - `#[php(name = "InterfaceName")]` or `#[php(change_case = snake_case)]`: Sets
355+
/// the name of the interface in PHP. The default is the `PascalCase` name of the
356+
/// interface.
357+
/// - `#[php(extends(ce = ce::throwable, stub = "\\Throwable"))]`
358+
/// to extends interface from other interface
359+
///
360+
/// ### Example
361+
///
362+
/// This example creates a PHP interface extend from php buildin Throwable.
363+
///
364+
/// ```rust,no_run,ignore
365+
/// # #![cfg_attr(windows, feature(abi_vectorcall))]
366+
/// # extern crate ext_php_rs;
367+
/// use ext_php_rs::prelude::*;
368+
///
369+
/// #[php_interface]
370+
/// #[php(extends(ce = ce::throwable, stub = "\\Throwable"))]
371+
/// #[php(name = "LibName\\Exception\\MyCustomDomainException")]
372+
/// pub trait MyCustomDomainException {
373+
/// fn createWithMessage(message: String) -> Self;
374+
/// }
375+
///
376+
/// #[php_module]
377+
/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
378+
/// module.interface::<PhpInterfaceMyCustomDomainException>()
379+
/// }
380+
/// # fn main() {}
381+
/// ```
382+
// END DOCS FROM interface.md
343383
#[proc_macro_attribute]
344384
pub fn php_interface(args: TokenStream, input: TokenStream) -> TokenStream {
345385
php_interface_internal(args.into(), input.into()).into()
@@ -1266,13 +1306,15 @@ mod tests {
12661306
}
12671307

12681308
fn runtime_expand_attr(path: &PathBuf) {
1309+
dbg!(path);
12691310
let file = std::fs::File::open(path).expect("Failed to open expand test file");
12701311
runtime_macros::emulate_attributelike_macro_expansion(
12711312
file,
12721313
&[
12731314
("php_class", php_class_internal as AttributeFn),
12741315
("php_const", php_const_internal as AttributeFn),
12751316
("php_enum", php_enum_internal as AttributeFn),
1317+
("php_interface", php_interface_internal as AttributeFn),
12761318
("php_extern", php_extern_internal as AttributeFn),
12771319
("php_function", php_function_internal as AttributeFn),
12781320
("php_impl", php_impl_internal as AttributeFn),
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#![feature(prelude_import)]
2+
#[prelude_import]
3+
use std::prelude::rust_2024::*;
4+
#[macro_use]
5+
extern crate std;
6+
use ext_php_rs::types::ZendClassObject;
7+
use ext_php_rs::php_interface;
8+
use ext_php_rs::zend::ce;
9+
pub trait EmptyObjectTrait {
10+
const HELLO: &'static str = "HELLO";
11+
const ONE: u64 = 12;
12+
fn void();
13+
fn non_static(&self, data: String) -> String;
14+
fn ref_to_like_this_class(
15+
&self,
16+
data: String,
17+
other: &ZendClassObject<PhpInterfaceEmptyObjectTrait>,
18+
) -> String;
19+
}
20+
pub struct PhpInterfaceEmptyObjectTrait;
21+
impl PhpInterfaceEmptyObjectTrait {
22+
pub const HELLO: &'static str = "HELLO";
23+
pub const ONE: u64 = 12;
24+
}
25+
impl ::ext_php_rs::class::RegisteredClass for PhpInterfaceEmptyObjectTrait {
26+
const CLASS_NAME: &'static str = "ExtPhpRs\\Interface\\EmptyObjectInterface";
27+
const BUILDER_MODIFIER: Option<
28+
fn(::ext_php_rs::builders::ClassBuilder) -> ::ext_php_rs::builders::ClassBuilder,
29+
> = None;
30+
const EXTENDS: Option<::ext_php_rs::class::ClassEntryInfo> = None;
31+
const FLAGS: ::ext_php_rs::flags::ClassFlags = ::ext_php_rs::flags::ClassFlags::Interface;
32+
const IMPLEMENTS: &'static [::ext_php_rs::class::ClassEntryInfo] = &[
33+
(ce::throwable, "\\Throwable"),
34+
];
35+
fn get_metadata() -> &'static ::ext_php_rs::class::ClassMetadata<Self> {
36+
static METADATA: ::ext_php_rs::class::ClassMetadata<
37+
PhpInterfaceEmptyObjectTrait,
38+
> = ::ext_php_rs::class::ClassMetadata::new();
39+
&METADATA
40+
}
41+
fn method_builders() -> Vec<
42+
(
43+
::ext_php_rs::builders::FunctionBuilder<'static>,
44+
::ext_php_rs::flags::MethodFlags,
45+
),
46+
> {
47+
<[_]>::into_vec(
48+
::alloc::boxed::box_new([
49+
(
50+
::ext_php_rs::builders::FunctionBuilder::new_abstract("void")
51+
.not_required(),
52+
::ext_php_rs::flags::MethodFlags::Public
53+
| ::ext_php_rs::flags::MethodFlags::Abstract
54+
| ::ext_php_rs::flags::MethodFlags::Static,
55+
),
56+
(
57+
::ext_php_rs::builders::FunctionBuilder::new_abstract("nonStatic")
58+
.arg(
59+
::ext_php_rs::args::Arg::new(
60+
"data",
61+
<String as ::ext_php_rs::convert::FromZvalMut>::TYPE,
62+
),
63+
)
64+
.not_required()
65+
.returns(
66+
<String as ::ext_php_rs::convert::IntoZval>::TYPE,
67+
false,
68+
<String as ::ext_php_rs::convert::IntoZval>::NULLABLE,
69+
),
70+
::ext_php_rs::flags::MethodFlags::Public
71+
| ::ext_php_rs::flags::MethodFlags::Abstract,
72+
),
73+
(
74+
::ext_php_rs::builders::FunctionBuilder::new_abstract(
75+
"refToLikeThisClass",
76+
)
77+
.arg(
78+
::ext_php_rs::args::Arg::new(
79+
"data",
80+
<String as ::ext_php_rs::convert::FromZvalMut>::TYPE,
81+
),
82+
)
83+
.arg(
84+
::ext_php_rs::args::Arg::new(
85+
"other",
86+
<&ZendClassObject<
87+
PhpInterfaceEmptyObjectTrait,
88+
> as ::ext_php_rs::convert::FromZvalMut>::TYPE,
89+
),
90+
)
91+
.not_required()
92+
.returns(
93+
<String as ::ext_php_rs::convert::IntoZval>::TYPE,
94+
false,
95+
<String as ::ext_php_rs::convert::IntoZval>::NULLABLE,
96+
),
97+
::ext_php_rs::flags::MethodFlags::Public
98+
| ::ext_php_rs::flags::MethodFlags::Abstract,
99+
),
100+
]),
101+
)
102+
}
103+
fn constructor() -> Option<::ext_php_rs::class::ConstructorMeta<Self>> {
104+
None
105+
}
106+
fn constants() -> &'static [(
107+
&'static str,
108+
&'static dyn ext_php_rs::convert::IntoZvalDyn,
109+
ext_php_rs::describe::DocComments,
110+
)] {
111+
use ::ext_php_rs::internal::class::PhpClassImpl;
112+
::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default()
113+
.get_constants()
114+
}
115+
fn get_properties<'a>() -> std::collections::HashMap<
116+
&'static str,
117+
::ext_php_rs::internal::property::PropertyInfo<'a, Self>,
118+
> {
119+
HashMap::new()
120+
}
121+
}
122+
impl ::ext_php_rs::internal::class::PhpClassImpl<PhpInterfaceEmptyObjectTrait>
123+
for ::ext_php_rs::internal::class::PhpClassImplCollector<PhpInterfaceEmptyObjectTrait> {
124+
fn get_methods(
125+
self,
126+
) -> ::std::vec::Vec<
127+
(
128+
::ext_php_rs::builders::FunctionBuilder<'static>,
129+
::ext_php_rs::flags::MethodFlags,
130+
),
131+
> {
132+
::alloc::vec::Vec::new()
133+
}
134+
fn get_method_props<'a>(
135+
self,
136+
) -> ::std::collections::HashMap<
137+
&'static str,
138+
::ext_php_rs::props::Property<'a, PhpInterfaceEmptyObjectTrait>,
139+
> {
140+
::core::panicking::panic("not yet implemented")
141+
}
142+
fn get_constructor(
143+
self,
144+
) -> ::std::option::Option<
145+
::ext_php_rs::class::ConstructorMeta<PhpInterfaceEmptyObjectTrait>,
146+
> {
147+
None
148+
}
149+
fn get_constants(
150+
self,
151+
) -> &'static [(
152+
&'static str,
153+
&'static dyn ::ext_php_rs::convert::IntoZvalDyn,
154+
&'static [&'static str],
155+
)] {
156+
&[
157+
("HELLO", &PhpInterfaceEmptyObjectTrait::HELLO, &[]),
158+
("ONE", &PhpInterfaceEmptyObjectTrait::ONE, &[]),
159+
]
160+
}
161+
}
162+
impl<'a> ::ext_php_rs::convert::FromZendObject<'a> for &'a PhpInterfaceEmptyObjectTrait {
163+
#[inline]
164+
fn from_zend_object(
165+
obj: &'a ::ext_php_rs::types::ZendObject,
166+
) -> ::ext_php_rs::error::Result<Self> {
167+
let obj = ::ext_php_rs::types::ZendClassObject::<
168+
PhpInterfaceEmptyObjectTrait,
169+
>::from_zend_obj(obj)
170+
.ok_or(::ext_php_rs::error::Error::InvalidScope)?;
171+
Ok(&**obj)
172+
}
173+
}
174+
impl<'a> ::ext_php_rs::convert::FromZendObjectMut<'a>
175+
for &'a mut PhpInterfaceEmptyObjectTrait {
176+
#[inline]
177+
fn from_zend_object_mut(
178+
obj: &'a mut ::ext_php_rs::types::ZendObject,
179+
) -> ::ext_php_rs::error::Result<Self> {
180+
let obj = ::ext_php_rs::types::ZendClassObject::<
181+
PhpInterfaceEmptyObjectTrait,
182+
>::from_zend_obj_mut(obj)
183+
.ok_or(::ext_php_rs::error::Error::InvalidScope)?;
184+
Ok(&mut **obj)
185+
}
186+
}
187+
impl<'a> ::ext_php_rs::convert::FromZval<'a> for &'a PhpInterfaceEmptyObjectTrait {
188+
const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Object(
189+
Some(
190+
<PhpInterfaceEmptyObjectTrait as ::ext_php_rs::class::RegisteredClass>::CLASS_NAME,
191+
),
192+
);
193+
#[inline]
194+
fn from_zval(zval: &'a ::ext_php_rs::types::Zval) -> ::std::option::Option<Self> {
195+
<Self as ::ext_php_rs::convert::FromZendObject>::from_zend_object(zval.object()?)
196+
.ok()
197+
}
198+
}
199+
impl<'a> ::ext_php_rs::convert::FromZvalMut<'a>
200+
for &'a mut PhpInterfaceEmptyObjectTrait {
201+
const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Object(
202+
Some(
203+
<PhpInterfaceEmptyObjectTrait as ::ext_php_rs::class::RegisteredClass>::CLASS_NAME,
204+
),
205+
);
206+
#[inline]
207+
fn from_zval_mut(
208+
zval: &'a mut ::ext_php_rs::types::Zval,
209+
) -> ::std::option::Option<Self> {
210+
<Self as ::ext_php_rs::convert::FromZendObjectMut>::from_zend_object_mut(
211+
zval.object_mut()?,
212+
)
213+
.ok()
214+
}
215+
}
216+
impl ::ext_php_rs::convert::IntoZendObject for PhpInterfaceEmptyObjectTrait {
217+
#[inline]
218+
fn into_zend_object(
219+
self,
220+
) -> ::ext_php_rs::error::Result<
221+
::ext_php_rs::boxed::ZBox<::ext_php_rs::types::ZendObject>,
222+
> {
223+
Ok(::ext_php_rs::types::ZendClassObject::new(self).into())
224+
}
225+
}
226+
impl ::ext_php_rs::convert::IntoZval for PhpInterfaceEmptyObjectTrait {
227+
const TYPE: ::ext_php_rs::flags::DataType = ::ext_php_rs::flags::DataType::Object(
228+
Some(
229+
<PhpInterfaceEmptyObjectTrait as ::ext_php_rs::class::RegisteredClass>::CLASS_NAME,
230+
),
231+
);
232+
const NULLABLE: bool = false;
233+
#[inline]
234+
fn set_zval(
235+
self,
236+
zv: &mut ::ext_php_rs::types::Zval,
237+
persistent: bool,
238+
) -> ::ext_php_rs::error::Result<()> {
239+
use ::ext_php_rs::convert::IntoZendObject;
240+
self.into_zend_object()?.set_zval(zv, persistent)
241+
}
242+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#[macro_use]
2+
extern crate ext_php_rs_derive;
3+
4+
use ext_php_rs::types::ZendClassObject;
5+
use ext_php_rs::php_interface;
6+
use ext_php_rs::zend::ce;
7+
8+
#[php_interface]
9+
#[php(extends(ce = ce::throwable, stub = "\\Throwable"))]
10+
#[php(name = "ExtPhpRs\\Interface\\EmptyObjectInterface")]
11+
pub trait EmptyObjectTrait
12+
{
13+
const HELLO: &'static str = "HELLO";
14+
15+
const ONE: u64 = 12;
16+
17+
fn void();
18+
19+
fn non_static(&self, data: String) -> String;
20+
21+
fn ref_to_like_this_class(
22+
&self,
23+
data: String,
24+
other: &ZendClassObject<PhpInterfaceEmptyObjectTrait>
25+
) -> String;
26+
}

tests/src/integration/interface/interface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
assert(interface_exists('ExtPhpRs\Interface\EmptyObjectInterface'), 'Interface not exist');
66

7-
assert(is_a('ExtPhpRs\Interface\EmptyObjectInterface', Throwable::class, true), 'Interface sould extend Throwable');
7+
assert(is_a('ExtPhpRs\Interface\EmptyObjectInterface', Throwable::class, true), 'Interface could extend Throwable');
88

99

1010
final class Test extends Exception implements ExtPhpRs\Interface\EmptyObjectInterface

0 commit comments

Comments
 (0)