Migrating to v0.14

New Macro Transition

The old macro system used a global state to be able to automatically register functions and classes when the #[php_module] attribute is used. However, global state can cause problems with incremental compilation and is not recommended.

To solve this, the macro system has been re-written but this will require changes to user code. This document summarises the changes.

There is no real changes on existing macros, however you will now need to register functions, classes, constants and startup function when declaring the module.

#[php_module(startup = "startup_function")]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .class::<TestClass>()
        .function(wrap_function!(hello_world))
        .constant(wrap_constant!(SOME_CONSTANT))
}

Functions

Mostly unchanged in terms of function definition, however you now need to register the function with the module builder:

use ext_php_rs::prelude::*;

#[php_function]
pub fn hello_world() -> &'static str {
    "Hello, world!"
}

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

Supported #[php] attributes:

  • #[php(name = "NEW_NAME")] - Renames the function
  • #[php(rename = case)] - Changes the case of the function name
  • #[php(vis = "public")] - Changes the visibility of the function
  • #[php(defaults(a = 5, test = 100))] - Sets default values for function arguments
  • #[php(variadic)] - Marks the function as variadic. The last argument must be a &[&Zval]

Classes

Mostly unchanged in terms of the class and impl definitions, however you now need to register the classes with the module builder:

use ext_php_rs::prelude::*;

#[php_class]
#[derive(Debug)]
pub struct TestClass {
    #[php(prop)]
    a: i32,
    #[php(prop)]
    b: i32,
}

#[php_impl]
impl TestClass {
    #[php(name = "NEW_CONSTANT_NAME")]
    pub const SOME_CONSTANT: i32 = 5;
    pub const SOME_OTHER_STR: &'static str = "Hello, world!";

    pub fn __construct(a: i32, b: i32) -> Self {
        Self { a: a + 10, b: b + 10 }
    }

    #[php(defaults(a = 5, test = 100))]
    pub fn test_camel_case(&self, a: i32, test: i32) {
        println!("a: {} test: {}", a, test);
    }

    fn x(&self) -> i32 {
        5
    }

    pub fn builder_pattern(
        self_: &mut ZendClassObject<TestClass>,
    ) -> &mut ZendClassObject<TestClass> {
        dbg!(self_)
    }
}

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .class::<TestClass>()
}

Supported #[php] attributes (struct):

  • #[php(name = "NEW_NAME")] - Renames the class
  • #[php(rename = case)] - Changes the case of the class name
  • #[php(vis = "public")] - Changes the visibility of the class
  • #[php(extends = "ParentClass")] - Extends a parent class
  • #[php(implements = "Interface")] - Implements an interface
  • #[php(prop)] - Marks a field as a property

Supported #[php] attributes (impl):

  • #[php(rename_consts = case)] - Changes the case of the constant names. Can be overridden by attributes on the constants.
  • #[php(rename_methods = case)] - Changes the case of the method names. Can be overridden by attributes on the methods.

For elements in the #[php_impl] block see the respective function and constant attributes.

Constants

Mostly unchanged in terms of constant definition, however you now need to register the constant with the module builder:

use ext_php_rs::prelude::*;

#[php_const]
const SOME_CONSTANT: i32 = 100;

#[php_const]
#[php(name = "HELLO_WORLD")]
const SOME_OTHER_CONSTANT: &'static str = "Hello, world!";

#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .constant(wrap_constant!(SOME_CONSTANT)) // SOME_CONSTANT = 100
        .constant(wrap_constant!(SOME_OTHER_CONSTANT)) // HELLO_WORLD = "Hello, world!"
        .constant(("CONST_NAME", SOME_CONSTANT, &[])) // CONST_NAME = 100
}

Supported #[php] attributes:

  • #[php(name = "NEW_NAME")] - Renames the constant
  • #[php(rename = case)] - Changes the case of the constant name
  • #[php(vis = "public")] - Changes the visibility of the constant

Extern

No changes.

use ext_php_rs::prelude::*;

#[php_extern]
extern "C" {
    fn phpinfo() -> bool;
}

fn some_rust_func() {
    let x = unsafe { phpinfo() };
    println!("phpinfo: {x}");
}

Startup Function

The #[php_startup] macro has been deprecated. Instead, define a function with the signature fn(ty: i32, mod_num: i32) -> i32 and provide the function name

use ext_php_rs::prelude::*;

fn startup_function(ty: i32, mod_num: i32) -> i32 {
    0
}

#[php_module(startup = "startup_function")]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module
}

#[php] Attributes

Attributes like #[rename] or #[prop] have been moved to the #[php] attribute.

The #[php] attribute on an item are combined with each other. This means that the following variants are equivalent:

#[php(rename = case)]
#[php(vis = "public")]
#[php(rename = case, vis = "public")]