ext-php-rs 0.15.10

Bindings for the Zend API to build PHP extensions natively in Rust.
Documentation
# `#[php_interface]` Attribute

You can export a `Trait` block to PHP. This exports all methods as well as
constants to PHP on the interface. Trait method SHOULD NOT contain default
implementations, as these are not supported in PHP interfaces.

## Options

By default all constants are renamed to `UPPER_CASE` and all methods are renamed to
`camelCase`. This can be changed by passing the `change_method_case` and
`change_constant_case` as `#[php]` attributes on the `impl` block. The options are:

- `#[php(change_method_case = "snake_case")]` - Renames the method to snake case.
- `#[php(change_constant_case = "snake_case")]` - Renames the constant to snake case.

See the [`name` and `change_case`](./php.md#name-and-change_case) section for a list of all
available cases.

## Methods

See the [`php_impl`](./impl.md#)

## Constants

See the [`php_impl`](./impl.md#)

## Example

Define an example trait with methods and constant:

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::{prelude::*, types::ZendClassObject};


#[php_interface]
#[php(name = "Rust\\TestInterface")]
trait Test {
    const TEST: &'static str = "TEST";

    fn co();

    #[php(defaults(value = 0))]
    fn set_value(&mut self, value: i32);
}

#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .interface::<PhpInterfaceTest>()
}

# fn main() {}
```

Using our newly created interface in PHP:

```php
<?php

assert(interface_exists("Rust\TestInterface"));

class B implements Rust\TestInterface {

    public static function co() {}

    public function setValue(?int $value = 0) {

    }
}

```

## Interface Inheritance

PHP interfaces can extend other interfaces. You can achieve this in two ways:

### Using `#[php(extends(...))]`

Use the `extends` attribute to extend a built-in PHP interface or another Rust-defined interface.

For built-in PHP interfaces, use the explicit form:

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::prelude::*;
use ext_php_rs::zend::ce;

#[php_interface]
#[php(extends(ce = ce::throwable, stub = "\\Throwable"))]
#[php(name = "MyException")]
trait MyExceptionInterface {
    fn get_error_code(&self) -> i32;
}

# fn main() {}
```

For Rust-defined interfaces, you can use the simpler type syntax:

```rust,ignore
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::prelude::*;

#[php_interface]
trait BaseInterface {
    fn base_method(&self) -> i32;
}

#[php_interface]
#[php(extends(BaseInterface))]
trait ExtendedInterface {
    fn extended_method(&self) -> String;
}

# fn main() {}
```

### Using Rust Trait Bounds

You can also use Rust's trait bound syntax. When a trait marked with `#[php_interface]`
has supertraits, the PHP interface will automatically extend those parent interfaces:

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::prelude::*;

#[php_interface]
#[php(name = "Rust\\ParentInterface")]
trait ParentInterface {
    fn parent_method(&self) -> String;
}

// ChildInterface extends ParentInterface in PHP
#[php_interface]
#[php(name = "Rust\\ChildInterface")]
trait ChildInterface: ParentInterface {
    fn child_method(&self) -> String;
}

#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .interface::<PhpInterfaceParentInterface>()
        .interface::<PhpInterfaceChildInterface>()
}

# fn main() {}
```

In PHP:

```php
<?php

// ChildInterface extends ParentInterface
assert(is_a('Rust\ChildInterface', 'Rust\ParentInterface', true));
```

# `#[php_impl_interface]` Attribute

The `#[php_impl_interface]` attribute allows a Rust class to implement a custom PHP
interface defined with `#[php_interface]`. This creates a relationship where PHP's
`instanceof` and `is_a()` recognize the implementation.

**Key feature**: The macro automatically registers the trait methods as PHP methods
on the class. You don't need to duplicate them in a separate `#[php_impl]` block.

## Example

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::prelude::*;

// Define a custom interface
#[php_interface]
#[php(name = "Rust\\Greetable")]
trait Greetable {
    fn greet(&self) -> String;
}

// Define a class
#[php_class]
#[php(name = "Rust\\Greeter")]
pub struct Greeter {
    name: String,
}

#[php_impl]
impl Greeter {
    pub fn __construct(name: String) -> Self {
        Self { name }
    }

    // Note: No need to add greet() here - it's automatically
    // registered by #[php_impl_interface] below
}

// Implement the interface for the class
// This automatically registers greet() as a PHP method
#[php_impl_interface]
impl Greetable for Greeter {
    fn greet(&self) -> String {
        format!("Hello, {}!", self.name)
    }
}

#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
    module
        .interface::<PhpInterfaceGreetable>()
        .class::<Greeter>()
}

# fn main() {}
```

Using in PHP:

```php
<?php

$greeter = new Rust\Greeter("World");

// instanceof works
assert($greeter instanceof Rust\Greetable);

// is_a() works
assert(is_a($greeter, 'Rust\Greetable'));

// The greet() method is available (registered by #[php_impl_interface])
echo $greeter->greet(); // Output: Hello, World!

// Can be used as type hint
function greet(Rust\Greetable $obj): void {
    echo $obj->greet();
}

greet($greeter);
```

## When to Use

- Use `#[php_impl_interface]` for custom interfaces you define with `#[php_interface]`
- Use `#[php(implements(ce = ...))]` on `#[php_class]` for built-in PHP interfaces
  like `Iterator`, `ArrayAccess`, `Countable`, etc.

See the [Classes documentation](./classes.md#implementing-an-interface) for examples
of implementing built-in interfaces.

## Cross-Crate Support

The `#[php_impl_interface]` macro supports cross-crate interface discovery via the
[`inventory`](https://crates.io/crates/inventory) crate. This means you can define
an interface in one crate and implement it in another crate, and the implementation
will be automatically discovered at link time.

### Example: Defining an Interface in a Library Crate

First, create a library crate that defines the interface:

```toml
# my-interfaces/Cargo.toml
[package]
name = "my-interfaces"
version = "0.1.0"

[dependencies]
ext-php-rs = "0.15"
```

```rust,no_run,ignore
// my-interfaces/src/lib.rs
use ext_php_rs::prelude::*;

/// A serializable interface that can convert objects to JSON.
#[php_interface]
#[php(name = "MyInterfaces\\Serializable")]
pub trait Serializable {
    fn to_json(&self) -> String;
}

// Re-export the generated PHP interface struct for consumers
pub use PhpInterfaceSerializable;
```

### Example: Implementing the Interface in Another Crate

Now create your extension crate that implements the interface:

```toml
# my-extension/Cargo.toml
[package]
name = "my-extension"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
ext-php-rs = "0.15"
my-interfaces = { path = "../my-interfaces" }
```

```rust,no_run,ignore
// my-extension/src/lib.rs
use ext_php_rs::prelude::*;
use my_interfaces::Serializable;

#[php_class]
#[php(name = "MyExtension\\User")]
pub struct User {
    name: String,
    email: String,
}

#[php_impl]
impl User {
    pub fn __construct(name: String, email: String) -> Self {
        Self { name, email }
    }

    // Note: No need to add to_json() here - it's automatically
    // registered by #[php_impl_interface] below
}

// Register the interface implementation
// This automatically registers to_json() as a PHP method
#[php_impl_interface]
impl Serializable for User {
    fn to_json(&self) -> String {
        format!(r#"{{"name":"{}","email":"{}"}}"#, self.name, self.email)
    }
}

#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
    module
        // Register the interface from the library crate
        .interface::<my_interfaces::PhpInterfaceSerializable>()
        .class::<User>()
}
```

### Using in PHP

```php
<?php

use MyExtension\User;
use MyInterfaces\Serializable;

$user = new User("John", "john@example.com");

// instanceof works across crates
assert($user instanceof Serializable);

// Type hints work
function serialize_object(Serializable $obj): string {
    return $obj->toJson();
}

echo serialize_object($user);
// Output: {"name":"John","email":"john@example.com"}
```

### Important Notes

1. **Automatic method registration**: The `#[php_impl_interface]` macro automatically
   registers all trait methods as PHP methods on the class. You don't need to duplicate
   them in a `#[php_impl]` block.

2. **Interface registration**: The interface must be registered in the `#[php_module]`
   function using `.interface::<PhpInterfaceName>()`.

3. **Link-time discovery**: The `inventory` crate uses link-time registration for
   interface discovery, so all implementations are automatically discovered when the
   final binary is linked.