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:
- Most primitive integers (
i8
,i16
,i32
,i64
,u8
,u16
,u32
,u64
,usize
,isize
) - Double-precision floating point numbers (
f64
) bool
String
Vec<T>
andHashMap<String, T>
whereT: FromZval
.Binary<T>
for passing binary data as a string, whereT: Pack
.ZendCallable
for receiving PHP callables, not applicable for return values.Option<T>
whereT: FromZval
. When used as a parameter, the parameter will be deemed nullable, and will containNone
whennull
is passed. When used as a return type, ifNone
is returned theZval
will be set to null. Optional parameters must be of the typeOption<T>
.
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)
}