Attribute Macro ext_php_rs::php_function

source ·
#[php_function]
Expand description

Attribute used to annotate a function as a PHP function.

Only types that implement FromZval can be used as parameter and return types. These include but are not limited to the following:

Additionally, you are able to return a variant of Result<T, E>. T must implement IntoZval and E must implement Into<PhpException>. If an error variant is returned, a PHP exception is thrown using the PhpException struct contents.

You are able to implement FromZval on your own custom types to have arguments passed in seamlessly. Similarly, you can implement IntoZval on values that you want to be able to be returned from PHP functions.

Parameters may be deemed optional by passing the parameter name into the attribute options. Note that all parameters that are optional (which includes the given optional parameter as well as all parameters after) must be of the type Option<T>, where T is a valid type.

Generics are not supported.

Behind the scenes, an extern "C" wrapper function is generated, which is actually called by PHP. The first example function would be converted into a function which looks like so:

pub fn hello(name: String) -> String {
    format!("Hello, {}!", name)
}

pub extern "C" fn _internal_php_hello(ex: &mut ExecuteData, retval: &mut Zval) {
    let mut name = Arg::new("name", <String as FromZvalMut>::TYPE);
    let parser = ex.parser()
        .arg(&mut name)
        .parse();

    if parser.is_err() {
        return;
    }

    let result = hello(match name.val() {
        Some(val) => val,
        None => {
            PhpException::default("Invalid value given for argument `name`.".into())
                .throw()
                .expect("Failed to throw exception: Invalid value given for argument `name`.");
            return;
        }
    });

    match result.set_zval(retval, false) {
        Ok(_) => {},
        Err(e) => {
            let e: PhpException = e.into();
            e.throw().expect("Failed to throw exception: Failed to set return value.");
        }
    };
}

This allows the original function to continue being used while also being exported as a PHP function.

§Examples

Creating a simple function which will return a string. The function still must be declared in the PHP module to be able to call.

#[php_function]
pub fn hello(name: String) -> String {
    format!("Hello, {}!", name)
}

Parameters can also be deemed optional by passing the parameter name in the attribute options. This function takes one required parameter (name) and two optional parameters (description and age).

#[php_function(optional = "description")]
pub fn hello(name: String, description: Option<String>, age: Option<i32>) -> String {
    let mut response = format!("Hello, {}!", name);

    if let Some(description) = description {
        response.push_str(format!(" {}.", description).as_ref());
    }

    if let Some(age) = age {
        response.push_str(format!(" I am {} year(s) old.", age).as_ref());
    }

    response
}

Defaults can also be given in a similar fashion. For example, the above function could have default values for description and age by changing the attribute to the following:

#[php_function(optional = "description", defaults(description = "David", age = 10))]
pub fn hello(name: String, description: String, age: i32) -> String {
    format!("Hello, {}! {}. I am {} year(s) old.", name, description, age)
}