Skip to main content

php_function

Attribute Macro php_function 

Source
#[php_function]
Expand description

§#[php_function] Attribute

Used to annotate functions which should be exported to PHP. Note that this should not be used on class methods - see the #[php_impl] macro for that.

See the list of types that are valid as parameter and return types. If the caller passes a value that the parameter type does not accept, the call throws a TypeError.

A function accepts the name, change_case, defaults and optional options. PHP functions have no visibility, so #[php(vis = "...")] is a compile error on a function.

§Optional parameters

An Option<T> parameter accepts null and can be omitted. The macro reads this from the type through FromZvalMut::NULLABLE, so a type alias such as type MaybeAge = Option<i64> or the qualified path std::option::Option<i64> behaves like Option<i64>. The trailing run of parameters that are nullable or have a default value is optional.

ⓘ
use ext_php_rs::prelude::*;

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

    if let Some(age) = age {
        greeting += &format!(" You are {} years old.", age);
    }

    greeting
}

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module.function(wrap_function!(greet))
}

Default parameter values can also be set for optional parameters. This is done through the #[php(defaults)] attribute option. When an optional parameter has a default, it does not need to be a variant of Option. The default expression is converted into the parameter type with Into, and the type must implement StubLiteral, which every scalar, string, Option, Vec and HashMap does. The module renders the value once at load time into the stub file and into ReflectionParameter::getDefaultValue(). Each key in defaults must name a parameter of the function. If a key names no parameter, the build fails:

ⓘ
use ext_php_rs::prelude::*;

#[php_function]
#[php(defaults(offset = 0))]
pub fn rusty_strpos(haystack: &str, needle: &str, offset: i64) -> Option<usize> {
    let haystack: String = haystack.chars().skip(offset as usize).collect();
    haystack.find(needle)
}

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module.function(wrap_function!(rusty_strpos))
}

Note that if there is a non-optional argument after an argument that is a variant of Option<T>, the Option<T> argument will be deemed a nullable argument rather than an optional argument.

ⓘ
use ext_php_rs::prelude::*;

/// `age` will be deemed required and nullable rather than optional.
#[php_function]
pub fn greet(name: String, age: Option<i32>, description: String) -> String {
    let mut greeting = format!("Hello, {}!", name);

    if let Some(age) = age {
        greeting += &format!(" You are {} years old.", age);
    }

    greeting += &format!(" {}.", description);
    greeting
}

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module.function(wrap_function!(greet))
}

You can also specify the optional arguments if you want to have nullable arguments before optional arguments. This is done through the optional attribute option, which names the first optional parameter. PHP callers can omit that parameter and every parameter after it. Each of these parameters must be an Option<T>, have a default, or be the variadic &[T] tail. Otherwise the build fails with an error on the type of that parameter. If optional names no parameter, the build also fails:

ⓘ
use ext_php_rs::prelude::*;

/// `age` will be deemed required and nullable rather than optional,
/// while description will be optional.
#[php_function]
#[php(optional = "description")]
pub fn greet(name: String, age: Option<i32>, description: Option<String>) -> String {
    let mut greeting = format!("Hello, {}!", name);

    if let Some(age) = age {
        greeting += &format!(" You are {} years old.", age);
    }

    if let Some(description) = description {
        greeting += &format!(" {}.", description);
    }

    greeting
}

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module.function(wrap_function!(greet))
}

§Variadic Functions

A slice parameter &[T] in last position is variadic, the equivalent of the PHP ...$args syntax. T is any argument type: &[&Zval] receives the raw values, &[i64] receives converted integers. If one of the values cannot be converted into T, the call throws a TypeError. A variadic parameter that is not the last parameter is a compile error. The slice must be spelled in the signature; a type alias that hides the slice is not detected.

ⓘ
use ext_php_rs::{prelude::*, types::Zval};

/// This can be called from PHP as `add(1, 2, 3, 4, 5)`
#[php_function]
pub fn add(number: u32, numbers:&[&Zval]) -> u32 {
    // numbers is a slice of 4 Zvals all of type long
    number
}

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module.function(wrap_function!(add))
}

§Performance

The #[php_function] macro generates a zero-allocation fast path that reads arguments directly from the PHP call frame, matching how native C extensions parse parameters. This applies automatically — no configuration needed.

The same fast path is used for class methods defined with #[php_impl], including instance methods (&self, &mut self) and static methods.

ⓘ
// Fast path: standalone functions
#[php_function]
pub fn fast(name: String, age: Option<i32>) -> String { name }

// Fast path: parameters with defaults
#[php_function]
#[php(defaults(offset = 0))]
pub fn also_fast(haystack: &str, offset: i64) -> i64 { offset }
ⓘ
#[php_impl]
impl MyClass {
    // Fast path: static methods
    pub fn create(n: i32) -> Self { /* ... */ }

    // Fast path: instance methods
    pub fn get_value(&self, offset: i32) -> i32 { /* ... */ }
}

The only case that falls back to the runtime argument parser is when using variadic arguments (&[T], the Rust equivalent of PHP’s ...$args):

ⓘ
// Slower path: variadic arguments require runtime parsing
#[php_function]
pub fn add(first: u32, rest: &[&Zval]) -> u32 { first }

§Returning Result<T, E>

You can also return a Result from the function. The error variant will be translated into an exception and thrown. See the section on exceptions for more details.