ZvalConvert

The #[derive(ZvalConvert)] macro derives the FromZval and IntoZval traits on a struct or enum.

Structs

When used on a struct, the FromZendObject and IntoZendObject traits are also implemented, mapping fields to properties in both directions. All fields on the struct must implement FromZval as well. Generics are allowed on structs that use the derive macro, however, the implementation will add a FromZval bound to all generics types.

Examples

#![cfg_attr(windows, feature(abi_vectorcall))]
extern crate ext_php_rs;
use ext_php_rs::prelude::*;

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

#[php_function]
pub fn take_object(obj: ExampleClass) {
    dbg!(obj.a, obj.b, obj.c);
}

#[php_function]
pub fn give_object() -> ExampleClass<'static> {
    ExampleClass {
        a: 5,
        b: "String".to_string(),
        c: "Borrowed",
    }
}
#[php_module] pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { module }
fn main() {}

Calling from PHP:

<?php

$obj = new stdClass;
$obj->a = 5;
$obj->b = 'Hello, world!';
$obj->c = 'another string';

take_object($obj);
var_dump(give_object());

Another example involving generics:

#![cfg_attr(windows, feature(abi_vectorcall))]
extern crate ext_php_rs;
use ext_php_rs::prelude::*;

// T must implement both `PartialEq<i32>` and `FromZval`.
#[derive(Debug, ZvalConvert)]
pub struct CompareVals<T: PartialEq<i32>> {
    a: T,
    b: T
}

#[php_function]
pub fn take_object(obj: CompareVals<i32>) {
    dbg!(obj);
}
#[php_module] pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { module }
fn main() {}

Enums

When used on an enum, the FromZval implementation will treat the enum as a tagged union with a mixed datatype. This allows you to accept multiple types in a parameter, for example, a string and an integer.

The enum variants must not have named fields, and each variant must have exactly one field (the type to extract from the zval). Optionally, the enum may have one default variant with no data contained, which will be used when the rest of the variants could not be extracted from the zval.

The ordering of the variants in the enum is important, as the FromZval implementation will attempt to parse the zval data in order. For example, if you put a String variant before an integer variant, the integer would be converted to a string and passed as the string variant.

Examples

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

#![cfg_attr(windows, feature(abi_vectorcall))]
extern crate ext_php_rs;
use ext_php_rs::prelude::*;

#[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)
}
#[php_module] pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { module }
fn main() {}

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)