Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
# Changelog

## Unreleased

### BREAKING CHANGES

- *(macro)* [**breaking**] Functions and methods without an explicit return type now declare `void` as their PHP return type instead of having no return type (implicit `mixed`). This improves type safety but may cause errors if your function actually returns a value without declaring it. Magic methods `__destruct` and `__clone` are excluded as PHP forbids return types on them. See [migration guide](guide/src/migration-guides/v0.16.md).

### Added

- *(interface)* `#[php_impl_interface]` macro for implementing PHP interfaces via Rust traits [[#590](https://github.com/davidcole1340/ext-php-rs/issues/590)]

## [0.15.4](https://github.com/extphprs/ext-php-rs/compare/ext-php-rs-v0.15.3...ext-php-rs-v0.15.4) - 2026-01-26

### Added
- *(array)* Entry API (Issue #525) ([#611](https://github.com/extphprs/ext-php-rs/pull/611)) (by @kakserpom) [[#525](https://github.com/davidcole1340/ext-php-rs/issues/525)] [[#611](https://github.com/davidcole1340/ext-php-rs/issues/611)]
- *(class)* Readonly and final classes ([#639](https://github.com/extphprs/ext-php-rs/pull/639)) (by @kakserpom) [[#639](https://github.com/davidcole1340/ext-php-rs/issues/639)]
- *(core)* Add observer API ([#650](https://github.com/extphprs/ext-php-rs/pull/650)) (by @ptondereau) [[#650](https://github.com/davidcole1340/ext-php-rs/issues/650)]
- *(object)* Lazy ghost and Lazy Proxy ([#636](https://github.com/extphprs/ext-php-rs/pull/636)) (by @kakserpom) [[#636](https://github.com/davidcole1340/ext-php-rs/issues/636)]
- *(string)* Smartstring support ([#643](https://github.com/extphprs/ext-php-rs/pull/643)) (by @kakserpom) [[#643](https://github.com/davidcole1340/ext-php-rs/issues/643)]
- *(array)* Entry API (Issue #525) ([#611](https://github.com/extphprs/ext-php-rs/pull/611)) (by @kakserpom) [[#525](https://github.com/davidcole1340/ext-php-rs/issues/525)] [[#611](https://github.com/davidcole1340/ext-php-rs/issues/611)]
- *(class)* Readonly and final classes ([#639](https://github.com/extphprs/ext-php-rs/pull/639)) (by @kakserpom) [[#639](https://github.com/davidcole1340/ext-php-rs/issues/639)]
- *(core)* Add observer API ([#650](https://github.com/extphprs/ext-php-rs/pull/650)) (by @ptondereau) [[#650](https://github.com/davidcole1340/ext-php-rs/issues/650)]
- *(object)* Lazy ghost and Lazy Proxy ([#636](https://github.com/extphprs/ext-php-rs/pull/636)) (by @kakserpom) [[#636](https://github.com/davidcole1340/ext-php-rs/issues/636)]
- *(string)* Smartstring support ([#643](https://github.com/extphprs/ext-php-rs/pull/643)) (by @kakserpom) [[#643](https://github.com/davidcole1340/ext-php-rs/issues/643)]

### Fixed
- *(cargo-php)* Use runtime feature for cargo-php to avoid dynamic linking on musl ([#645](https://github.com/extphprs/ext-php-rs/pull/645)) (by @ptondereau) [[#645](https://github.com/davidcole1340/ext-php-rs/issues/645)]
- *(clippy)* V1.93.0 errors ([#648](https://github.com/extphprs/ext-php-rs/pull/648)) (by @ptondereau) [[#648](https://github.com/davidcole1340/ext-php-rs/issues/648)]
- *(deps)* Bump parking_lot required version to 0.12.3 (by @TobiasBengtsson) [[#640](https://github.com/davidcole1340/ext-php-rs/issues/640)]
- *(doc)* Update mdbook config ([#651](https://github.com/extphprs/ext-php-rs/pull/651)) (by @ptondereau) [[#651](https://github.com/davidcole1340/ext-php-rs/issues/651)]
- *(macro)* Refactor allowed and forbidden keywords to match PHP parser ([#647](https://github.com/extphprs/ext-php-rs/pull/647)) (by @ptondereau) [[#647](https://github.com/davidcole1340/ext-php-rs/issues/647)]
- *(windows)* Add fallback for 404 errors in windows build ([#649](https://github.com/extphprs/ext-php-rs/pull/649)) (by @ptondereau) [[#649](https://github.com/davidcole1340/ext-php-rs/issues/649)]
- Handle PHP mocks and subclasses of Rust-backed classes ([#653](https://github.com/extphprs/ext-php-rs/pull/653)) (by @ptondereau) [[#653](https://github.com/davidcole1340/ext-php-rs/issues/653)]
- *(cargo-php)* Use runtime feature for cargo-php to avoid dynamic linking on musl ([#645](https://github.com/extphprs/ext-php-rs/pull/645)) (by @ptondereau) [[#645](https://github.com/davidcole1340/ext-php-rs/issues/645)]
- *(clippy)* V1.93.0 errors ([#648](https://github.com/extphprs/ext-php-rs/pull/648)) (by @ptondereau) [[#648](https://github.com/davidcole1340/ext-php-rs/issues/648)]
- *(deps)* Bump parking_lot required version to 0.12.3 (by @TobiasBengtsson) [[#640](https://github.com/davidcole1340/ext-php-rs/issues/640)]
- *(doc)* Update mdbook config ([#651](https://github.com/extphprs/ext-php-rs/pull/651)) (by @ptondereau) [[#651](https://github.com/davidcole1340/ext-php-rs/issues/651)]
- *(macro)* Refactor allowed and forbidden keywords to match PHP parser ([#647](https://github.com/extphprs/ext-php-rs/pull/647)) (by @ptondereau) [[#647](https://github.com/davidcole1340/ext-php-rs/issues/647)]
- *(windows)* Add fallback for 404 errors in windows build ([#649](https://github.com/extphprs/ext-php-rs/pull/649)) (by @ptondereau) [[#649](https://github.com/davidcole1340/ext-php-rs/issues/649)]
- Handle PHP mocks and subclasses of Rust-backed classes ([#653](https://github.com/extphprs/ext-php-rs/pull/653)) (by @ptondereau) [[#653](https://github.com/davidcole1340/ext-php-rs/issues/653)]

## [0.15.3](https://github.com/extphprs/ext-php-rs/compare/ext-php-rs-v0.15.2...ext-php-rs-v0.15.3) - 2025-12-28

### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cfg-if = "1.0"
once_cell = "1.21"
anyhow = { version = "1", optional = true }
smartstring = { version = "1", optional = true }
inventory = "0.3"
ext-php-rs-derive = { version = "=0.11.7", path = "./crates/macros" }

[dev-dependencies]
Expand Down
18 changes: 18 additions & 0 deletions crates/macros/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,24 @@ fn generate_registered_class_impl(
::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constants()
}

#[inline]
fn interface_implementations() -> ::std::vec::Vec<::ext_php_rs::class::ClassEntryInfo> {
let my_type_id = ::std::any::TypeId::of::<Self>();
::ext_php_rs::inventory::iter::<::ext_php_rs::internal::class::InterfaceRegistration>()
.filter(|reg| reg.class_type_id == my_type_id)
.map(|reg| (reg.interface_getter)())
.collect()
}

#[inline]
fn interface_method_implementations() -> ::std::vec::Vec<(
::ext_php_rs::builders::FunctionBuilder<'static>,
::ext_php_rs::flags::MethodFlags,
)> {
use ::ext_php_rs::internal::class::InterfaceMethodsProvider;
::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_interface_methods()
}

#default_init_impl
}
}
Expand Down
79 changes: 46 additions & 33 deletions crates/macros/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,46 +317,59 @@ impl<'a> Function<'a> {
}
}

fn build_returns(&self, call_type: Option<&CallType>) -> Option<TokenStream> {
self.output.cloned().map(|mut output| {
output.drop_lifetimes();

// If returning &Self or &mut Self from a method, use the class type
// for return type information since we return `this` (ZendClassObject)
if returns_self_ref(self.output)
&& let Some(CallType::Method { class, .. }) = call_type
fn build_returns(&self, call_type: Option<&CallType>) -> TokenStream {
let Some(output) = self.output.cloned() else {
// PHP magic methods __destruct and __clone cannot have return types
// (only applies to class methods, not standalone functions)
if matches!(call_type, Some(CallType::Method { .. }))
&& (self.name == "__destruct" || self.name == "__clone")
{
return quote! {
.returns(
<&mut ::ext_php_rs::types::ZendClassObject<#class> as ::ext_php_rs::convert::IntoZval>::TYPE,
false,
<&mut ::ext_php_rs::types::ZendClassObject<#class> as ::ext_php_rs::convert::IntoZval>::NULLABLE,
)
};
return quote! {};
}
// No return type means void in PHP
return quote! {
.returns(::ext_php_rs::flags::DataType::Void, false, false)
};
};

// If returning Self (new instance) from a method, replace Self with
// the actual class type since Self won't resolve in generated code
if returns_self(self.output)
&& let Some(CallType::Method { class, .. }) = call_type
{
return quote! {
.returns(
<#class as ::ext_php_rs::convert::IntoZval>::TYPE,
false,
<#class as ::ext_php_rs::convert::IntoZval>::NULLABLE,
)
};
}
let mut output = output;
output.drop_lifetimes();

quote! {
// If returning &Self or &mut Self from a method, use the class type
// for return type information since we return `this` (ZendClassObject)
if returns_self_ref(self.output)
&& let Some(CallType::Method { class, .. }) = call_type
{
return quote! {
.returns(
<#output as ::ext_php_rs::convert::IntoZval>::TYPE,
<&mut ::ext_php_rs::types::ZendClassObject<#class> as ::ext_php_rs::convert::IntoZval>::TYPE,
false,
<#output as ::ext_php_rs::convert::IntoZval>::NULLABLE,
<&mut ::ext_php_rs::types::ZendClassObject<#class> as ::ext_php_rs::convert::IntoZval>::NULLABLE,
)
}
})
};
}

// If returning Self (new instance) from a method, replace Self with
// the actual class type since Self won't resolve in generated code
if returns_self(self.output)
&& let Some(CallType::Method { class, .. }) = call_type
{
return quote! {
.returns(
<#class as ::ext_php_rs::convert::IntoZval>::TYPE,
false,
<#class as ::ext_php_rs::convert::IntoZval>::NULLABLE,
)
};
}

quote! {
.returns(
<#output as ::ext_php_rs::convert::IntoZval>::TYPE,
false,
<#output as ::ext_php_rs::convert::IntoZval>::NULLABLE,
)
}
}

fn build_result(
Expand Down
Loading
Loading