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))
}

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 {
    #[prop]
    a: i32,
    #[prop]
    b: i32,
}

#[php_impl]
impl TestClass {
    #[rename("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 }
    }

    #[optional(test)]
    #[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>()
}

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_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .constant(wrap_constant!(SOME_CONSTANT)) // SOME_CONSTANT = 100
        .constant(("CONST_NAME", SOME_CONSTANT, &[])) // CONST_NAME = 100
}

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
}