Derive Macro ext_php_rs::ZvalConvert

source ·
#[derive(ZvalConvert)]
Expand description

Derives the traits required to convert a struct or enum to and from a Zval. Both FromZval and IntoZval are implemented on types which use this macro.

§Structs

When the macro is used on a struct, the FromZendObject and IntoZendObject traits are also implemented, and will attempt to retrieve values for the struct fields from the objects properties. This can be useful when you expect some arbitrary object (of which the type does not matter), but you care about the value of the properties.

All properties must implement FromZval and IntoZval themselves. Generics are supported, however, a FromZval and IntoZval bound will be added. If one property cannot be retrieved from the object, the whole conversion will fail.

§Examples

Basic example with some primitive PHP type.

#[derive(Debug, ZvalConvert)]
pub struct ExampleStruct<'a> {
    a: i32,
    b: String,
    c: &'a str
}

#[php_function]
pub fn take_object(obj: ExampleStruct) {
    dbg!(obj);
}

#[php_function]
pub fn give_object() -> ExampleStruct<'static> {
    ExampleStruct {
        a: 5,
        b: "Hello, world!".into(),
        c: "Static string",
    }
}

Can be used in PHP:

$obj = (object) [
    'a' => 5,
    'b' => 'Hello, world!',
    'c' => 'asdf',
];
take_object($obj);
var_dump(give_object());

Another example involving generics:

#[derive(Debug, ZvalConvert)]
pub struct CompareVals<T: PartialEq<i32>> {
    a: T,
    b: T
}

#[php_function]
pub fn take_object(obj: CompareVals<i32>) {
    dbg!(obj);
}

§Enums

When the macro is used on an enum, the FromZval and IntoZval implementations will treat the enum as a tagged union with a mixed datatype. This allows you to accept two different types in a parameter, for example, a string and an integer.

The enum variants must not have named fields (i.e. not in the form of a struct), and must have exactly one field, the type to extract from the Zval. Optionally, the enum may have a single default, empty variant, which is used when the Zval did not contain any data to fill the other variants. This empty variant is equivalent to null in PHP.

The ordering of the enum variants is important, as the Zval contents is matched in order of the variants. For example, Zval::string will attempt to read a string from the Zval, and if the Zval contains a long, the long will be converted to a string. If a string variant was placed above an integer variant in the enum, the integer would be converted into a string and passed as the string variant.

§Examples

Basic example showing the importance of variant ordering and default field:

#[derive(Debug, ZvalConvert)]
pub enum UnionExample<'a> {
    Long(u64), // Long
    ProperStr(&'a str), // Actual string - not a converted value
    ParsedStr(String), // Potentially parsed string, i.e. a double
    None // Zval did not contain anything that could be parsed above
}

#[php_function]
pub fn test_union(val: UnionExample) {
    dbg!(val);
}

#[php_function]
pub fn give_union() -> UnionExample<'static> {
    UnionExample::Long(5)
}

Use in PHP:

test_union(5); // UnionExample::Long(5)
test_union("Hello, world!"); // UnionExample::ProperStr("Hello, world!")
test_union(5.66666); // UnionExample::ParsedStr("5.6666")
test_union(null); // UnionExample::None
var_dump(give_union()); // int(5)