# 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.

```rs
#[php_module]
#[php(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:

```rs
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(change_case = 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:

```rs
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(change_case = case)]` - Changes the case of the class name
- `#[php(vis = "public")]` - Changes the visibility of the class
- `#[php(extends(ce = ce_fn, stub = "ParentClass")]` - Extends a parent class
- `#[php(implements(ce = ce_fn, stub = "Interface"))]` - Implements an interface
- `#[php(prop)]` - Marks a field as a property

**Supported `#[php]` attributes (`impl`):**
- `#[php(change_constant_case = case)]` - Changes the case of the constant names. Can be overridden by attributes on the constants.
- `#[php(change_method_case = 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.

#### Extends and Implements

Extends and implements are now taking a second parameter which is the
`stub` name. This is the name of the class or interface in PHP.

This value is only used for stub generation and is not used for the class name in Rust.

### Constants

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

```rs
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(change_case = case)]` - Changes the case of the constant name
- `#[php(vis = "public")]` - Changes the visibility of the constant

### Extern

No changes.

```rs
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

```rs
use ext_php_rs::prelude::*;

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

#[php_module]
#[php(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:
```rs
#[php(change_case = case)]
#[php(vis = "public")]
```

```rs
#[php(change_case = case, vis = "public")]
```

### Renaming and Case Changes

Default case was adjusted to match PSR standards:
- Class names are now `PascalCase`
- Property names are now `camelCase`
- Method names are now `camelCase`
- Constant names are now `UPPER_CASE`
- Function names are now `snake_case`

This can be changed using the `change_case` attribute on the item.
Additionally, the `change_method_case` and `change_constant_case` attributes can be used
to change the case of all methods and constants in a class.

#### `name` vs `change_case`

Previously the (re)name parameter was used to rename items. This has been
unified to use `name` to set the name of an item to a string literal. The
`change_case` parameter is now used to change the case of the name.

```rs
#[php(name = "NEW_NAME")]
#[php(change_case = snake_case)]]
```

Available cases are:
- `snake_case`
- `PascalCase`
- `camelCase`
- `UPPER_CASE`
- `none` - No change
