pub struct {{ struct_name }} {
php_obj: *mut ext_php_rs::types::ZendObject,
cached_name: String,
}
// SAFETY: PHP objects are single-threaded; the bridge is used
// only within a single PHP request, never across threads.
unsafe impl Send for {{ struct_name }} {}
unsafe impl Sync for {{ struct_name }} {}
impl Clone for {{ struct_name }} {
fn clone(&self) -> Self {
// SAFETY: Increment refcount for the cloned reference.
// The object pointer is guaranteed valid within the request.
unsafe {
if !self.php_obj.is_null() {
(*self.php_obj).inc_count();
}
}
Self {
php_obj: self.php_obj,
cached_name: self.cached_name.clone(),
}
}
}
impl Drop for {{ struct_name }} {
fn drop(&mut self) {
// SAFETY: Decrement refcount when the bridge is dropped.
// The object pointer is guaranteed valid until this point.
unsafe {
if !self.php_obj.is_null() {
(*self.php_obj).dec_count();
}
}
}
}
impl std::fmt::Debug for {{ struct_name }} {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{ struct_name }}")
}
}
impl {{ struct_name }} {
pub fn new(php_obj: &mut ext_php_rs::types::ZendObject) -> Self {
// SAFETY: Increment refcount to keep the object alive.
// The object pointer is valid within the current request context.
unsafe {
php_obj.inc_count();
}
let cached_name = php_obj
.try_call_method("name", vec![])
.ok()
.and_then(|v| v.string())
.unwrap_or("unknown".into())
.to_string();
Self {
php_obj: php_obj as *mut _,
cached_name,
}
}
}