use crate::{
arrays::ZArr,
errors::{ClassNotFoundError, InitializeObjectError, Throwable},
functions::{Function, FunctionEntry, HandlerMap, Method, MethodEntity},
modules::global_module,
objects::{StateObj, StateObject, ZObject},
strings::ZStr,
sys::*,
types::Scalar,
utils::ensure_end_with_zero,
values::ZVal,
};
use std::{
any::Any,
cell::RefCell,
ffi::{CString, c_char, c_void},
fmt::Debug,
marker::PhantomData,
mem::{ManuallyDrop, replace, size_of, transmute, zeroed},
os::raw::c_int,
ptr::{self, null, null_mut},
rc::Rc,
slice,
};
#[derive(Clone)]
#[repr(transparent)]
pub struct ClassEntry {
inner: zend_class_entry,
_p: PhantomData<*mut ()>,
}
impl ClassEntry {
#[inline]
pub unsafe fn from_ptr<'a>(ptr: *const zend_class_entry) -> &'a Self {
unsafe { (ptr as *const Self).as_ref().expect("ptr should't be null") }
}
#[inline]
pub unsafe fn try_from_ptr<'a>(ptr: *const zend_class_entry) -> Option<&'a Self> {
unsafe { (ptr as *const Self).as_ref() }
}
#[inline]
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_class_entry) -> &'a mut Self {
unsafe { (ptr as *mut Self).as_mut().expect("ptr should't be null") }
}
#[inline]
pub unsafe fn try_from_mut_ptr<'a>(ptr: *mut zend_class_entry) -> Option<&'a mut Self> {
unsafe { (ptr as *mut Self).as_mut() }
}
pub const fn as_ptr(&self) -> *const zend_class_entry {
&self.inner
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut zend_class_entry {
&mut self.inner
}
pub fn from_globals(class_name: impl AsRef<str>) -> crate::Result<&'static Self> {
let name = class_name.as_ref();
let ptr: *mut Self = find_global_class_entry_ptr(name).cast();
unsafe {
ptr.as_ref().ok_or_else(|| {
crate::Error::ClassNotFound(ClassNotFoundError::new(name.to_string()))
})
}
}
pub fn new_object(&self, arguments: impl AsMut<[ZVal]>) -> crate::Result<ZObject> {
let mut object = self.init_object()?;
object.call_construct(arguments)?;
Ok(object)
}
pub fn init_object(&self) -> crate::Result<ZObject> {
unsafe {
let ptr = self.as_ptr() as *mut _;
let mut val = ZVal::default();
if !phper_object_init_ex(val.as_mut_ptr(), ptr) {
Err(InitializeObjectError::new(self.get_name().to_str()?.to_owned()).into())
} else {
let mut val = ManuallyDrop::new(val);
let ptr = phper_z_obj_p(val.as_mut_ptr());
Ok(ZObject::from_raw_cast(ptr))
}
}
}
pub fn get_name(&self) -> &ZStr {
unsafe { ZStr::from_ptr(self.inner.name) }
}
pub fn has_method(&self, method_name: &str) -> bool {
unsafe {
let function_table = ZArr::from_ptr(&self.inner.function_table);
function_table.exists(method_name)
}
}
pub fn is_instance_of(&self, parent: &ClassEntry) -> bool {
unsafe { phper_instanceof_function(self.as_ptr(), parent.as_ptr()) }
}
pub fn get_static_property(&self, name: impl AsRef<str>) -> Option<&ZVal> {
let ptr = self.as_ptr() as *mut _;
let prop = Self::inner_get_static_property(ptr, name);
unsafe { ZVal::try_from_ptr(prop) }
}
pub fn set_static_property(&self, name: impl AsRef<str>, val: impl Into<ZVal>) -> Option<ZVal> {
let ptr = self.as_ptr() as *mut _;
let prop = Self::inner_get_static_property(ptr, name);
let prop = unsafe { ZVal::try_from_mut_ptr(prop) };
prop.map(|prop| replace(prop, val.into()))
}
fn inner_get_static_property(scope: *mut zend_class_entry, name: impl AsRef<str>) -> *mut zval {
let name = name.as_ref();
unsafe {
#[allow(clippy::useless_conversion)]
zend_read_static_property(scope, name.as_ptr().cast(), name.len(), true.into())
}
}
}
impl Debug for ClassEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ClassEntry")
.field(&self.get_name().to_c_str())
.finish()
}
}
impl PartialEq for ClassEntry {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self as *const _, other as *const _)
}
}
impl Eq for ClassEntry {}
#[allow(clippy::useless_conversion)]
fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
let name = name.as_ref();
let name = name.to_lowercase();
unsafe {
phper_zend_hash_str_find_ptr(
compiler_globals.class_table,
name.as_ptr().cast(),
name.len().try_into().unwrap(),
)
.cast()
}
}
#[derive(Clone)]
pub(crate) enum InnerClassEntry {
Ptr(*const zend_class_entry),
Name(String),
}
pub struct StateClass<T: ?Sized> {
inner: Rc<RefCell<InnerClassEntry>>,
_p: PhantomData<T>,
}
impl StateClass<[()]> {
pub fn from_name(name: impl Into<String>) -> Self {
Self {
inner: Rc::new(RefCell::new(InnerClassEntry::Name(name.into()))),
_p: PhantomData,
}
}
}
impl<T: ?Sized> StateClass<T> {
fn null() -> Self {
Self {
inner: Rc::new(RefCell::new(InnerClassEntry::Ptr(null()))),
_p: PhantomData,
}
}
fn bind(&self, ptr: *mut zend_class_entry) {
match &mut *self.inner.borrow_mut() {
InnerClassEntry::Ptr(p) => {
*p = ptr;
}
InnerClassEntry::Name(_) => {
unreachable!("Cannot bind() an StateClass created with from_name()");
}
}
}
pub fn as_class_entry(&self) -> &ClassEntry {
let inner = self.inner.borrow().clone();
match inner {
InnerClassEntry::Ptr(ptr) => unsafe { ClassEntry::from_ptr(ptr) },
InnerClassEntry::Name(name) => {
let entry = ClassEntry::from_globals(name).unwrap();
*self.inner.borrow_mut() = InnerClassEntry::Ptr(entry.as_ptr());
entry
}
}
}
}
impl<T: 'static> StateClass<T> {
pub fn new_object(&self, arguments: impl AsMut<[ZVal]>) -> crate::Result<StateObject<T>> {
self.as_class_entry()
.new_object(arguments)
.map(ZObject::into_raw_cast)
.map(StateObject::<T>::from_raw_object)
}
pub fn init_object(&self) -> crate::Result<StateObject<T>> {
self.as_class_entry()
.init_object()
.map(ZObject::into_raw_cast)
.map(StateObject::<T>::from_raw_object)
}
}
impl<T> Clone for StateClass<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_p: self._p,
}
}
}
#[derive(Clone)]
pub struct Interface {
inner: Rc<RefCell<InnerClassEntry>>,
}
impl Interface {
fn null() -> Self {
Self {
inner: Rc::new(RefCell::new(InnerClassEntry::Ptr(null()))),
}
}
pub fn from_name(name: impl Into<String>) -> Self {
Self {
inner: Rc::new(RefCell::new(InnerClassEntry::Name(name.into()))),
}
}
fn bind(&self, ptr: *mut zend_class_entry) {
match &mut *self.inner.borrow_mut() {
InnerClassEntry::Ptr(p) => {
*p = ptr;
}
InnerClassEntry::Name(_) => {
unreachable!("Cannot bind() an Interface created with from_name()");
}
}
}
pub fn as_class_entry(&self) -> &ClassEntry {
let inner = self.inner.borrow().clone();
match inner {
InnerClassEntry::Ptr(ptr) => unsafe { ClassEntry::from_ptr(ptr) },
InnerClassEntry::Name(name) => {
let entry = ClassEntry::from_globals(name).unwrap();
*self.inner.borrow_mut() = InnerClassEntry::Ptr(entry.as_ptr());
entry
}
}
}
}
pub(crate) type StateConstructor = dyn Fn() -> *mut dyn Any;
pub(crate) type StateCloner = dyn Fn(*const dyn Any) -> *mut dyn Any;
pub struct ClassEntity<T: 'static> {
class_name: CString,
state_constructor: Rc<StateConstructor>,
method_entities: Vec<MethodEntity>,
property_entities: Vec<PropertyEntity>,
parent: Option<StateClass<[()]>>,
interfaces: Vec<Interface>,
constants: Vec<ConstantEntity>,
bound_class: StateClass<T>,
state_cloner: Option<Rc<StateCloner>>,
_p: PhantomData<(*mut (), T)>,
}
impl ClassEntity<()> {
pub fn new(class_name: impl Into<String>) -> Self {
Self::new_with_state_constructor(class_name, || ())
}
}
impl<T: Default + 'static> ClassEntity<T> {
pub fn new_with_default_state_constructor(class_name: impl Into<String>) -> Self {
Self::new_with_state_constructor(class_name, Default::default)
}
}
impl<T: 'static> ClassEntity<T> {
pub fn new_with_state_constructor(
class_name: impl Into<String>, state_constructor: impl Fn() -> T + 'static,
) -> Self {
Self {
class_name: ensure_end_with_zero(class_name),
state_constructor: Rc::new(move || {
let state = state_constructor();
let boxed = Box::new(state) as Box<dyn Any>;
Box::into_raw(boxed)
}),
method_entities: Vec::new(),
property_entities: Vec::new(),
parent: None,
interfaces: Vec::new(),
constants: Vec::new(),
bound_class: StateClass::null(),
state_cloner: None,
_p: PhantomData,
}
}
pub fn add_method<F, Z, E>(
&mut self, name: impl Into<String>, vis: Visibility, handler: F,
) -> &mut MethodEntity
where
F: Fn(&mut StateObj<T>, &mut [ZVal]) -> Result<Z, E> + 'static,
Z: Into<ZVal> + 'static,
E: Throwable + 'static,
{
self.method_entities.push(MethodEntity::new(
name,
Some(Rc::new(Method::new(handler))),
vis,
));
self.method_entities.last_mut().unwrap()
}
pub fn add_static_method<F, Z, E>(
&mut self, name: impl Into<String>, vis: Visibility, handler: F,
) -> &mut MethodEntity
where
F: Fn(&mut [ZVal]) -> Result<Z, E> + 'static,
Z: Into<ZVal> + 'static,
E: Throwable + 'static,
{
let mut entity = MethodEntity::new(name, Some(Rc::new(Function::new(handler))), vis);
entity.set_vis_static();
self.method_entities.push(entity);
self.method_entities.last_mut().unwrap()
}
pub fn add_abstract_method(
&mut self, name: impl Into<String>, vis: Visibility,
) -> &mut MethodEntity {
let mut entity = MethodEntity::new(name, None, vis);
entity.set_vis_abstract();
self.method_entities.push(entity);
self.method_entities.last_mut().unwrap()
}
pub fn add_property(
&mut self, name: impl Into<String>, visibility: Visibility, value: impl Into<Scalar>,
) {
self.property_entities
.push(PropertyEntity::new(name, visibility, value));
}
pub fn add_static_property(
&mut self, name: impl Into<String>, visibility: Visibility, value: impl Into<Scalar>,
) {
let mut entity = PropertyEntity::new(name, visibility, value);
entity.set_vis_static();
self.property_entities.push(entity);
}
pub fn add_constant(&mut self, name: impl Into<String>, value: impl Into<Scalar>) {
let constant = ConstantEntity::new(name, value);
self.constants.push(constant);
}
pub fn extends<S: ?Sized>(&mut self, parent: StateClass<S>) {
self.parent = Some(unsafe { transmute::<StateClass<S>, StateClass<[()]>>(parent) });
}
pub fn implements(&mut self, interface: Interface) {
self.interfaces.push(interface);
}
pub fn state_cloner(&mut self, clone_fn: impl Fn(&T) -> T + 'static) {
self.state_cloner = Some(Rc::new(move |src| {
let src = unsafe {
src.as_ref()
.unwrap()
.downcast_ref::<T>()
.expect("cast Any to T failed")
};
let dest = clone_fn(src);
let boxed = Box::new(dest) as Box<dyn Any>;
Box::into_raw(boxed)
}));
}
#[allow(clippy::useless_conversion)]
pub(crate) unsafe fn init(&self) -> *mut zend_class_entry {
unsafe {
let parent: *mut zend_class_entry = self
.parent
.as_ref()
.map(|parent| parent.as_class_entry())
.map(|entry| entry.as_ptr() as *mut _)
.unwrap_or(null_mut());
let class_ce = phper_init_class_entry_ex(
self.class_name.as_ptr().cast(),
self.class_name.as_bytes().len().try_into().unwrap(),
self.function_entries(),
Some(class_init_handler),
parent.cast(),
);
self.bound_class.bind(class_ce);
for interface in &self.interfaces {
let interface_ce = interface.as_class_entry().as_ptr();
zend_class_implements(class_ce, 1, interface_ce);
}
for constant in &self.constants {
add_class_constant(class_ce, constant);
}
*phper_get_create_object(class_ce) = Some(create_object);
class_ce
}
}
pub(crate) unsafe fn declare_properties(&self, ce: *mut zend_class_entry) {
for property in &self.property_entities {
property.declare(ce);
}
}
unsafe fn function_entries(&self) -> *const zend_function_entry {
unsafe {
let mut methods = self
.method_entities
.iter()
.map(|method| FunctionEntry::from_method_entity(method))
.collect::<Vec<_>>();
methods.push(zeroed::<zend_function_entry>());
methods.push(self.take_state_constructor_into_function_entry());
methods.push(self.take_state_cloner_into_function_entry());
Box::into_raw(methods.into_boxed_slice()).cast()
}
}
unsafe fn take_state_constructor_into_function_entry(&self) -> zend_function_entry {
unsafe {
let mut entry = zeroed::<zend_function_entry>();
let ptr = &mut entry as *mut _ as *mut *const StateConstructor;
let state_constructor = Rc::into_raw(self.state_constructor.clone());
ptr.write(state_constructor);
entry
}
}
unsafe fn take_state_cloner_into_function_entry(&self) -> zend_function_entry {
unsafe {
let mut entry = zeroed::<zend_function_entry>();
let ptr = &mut entry as *mut _ as *mut *const StateCloner;
if let Some(state_cloner) = &self.state_cloner {
let state_constructor = Rc::into_raw(state_cloner.clone());
ptr.write(state_constructor);
}
entry
}
}
pub(crate) fn handler_map(&self) -> HandlerMap {
self.method_entities
.iter()
.filter_map(|method| {
method.handler.as_ref().map(|handler| {
(
(Some(self.class_name.clone()), method.name.clone()),
handler.clone(),
)
})
})
.collect()
}
#[inline]
pub fn bound_class(&self) -> StateClass<T> {
self.bound_class.clone()
}
}
unsafe extern "C" fn class_init_handler(
class_ce: *mut zend_class_entry, argument: *mut c_void,
) -> *mut zend_class_entry {
unsafe {
let parent = argument as *mut zend_class_entry;
if parent.is_null() {
zend_register_internal_class(class_ce)
} else {
zend_register_internal_class_ex(class_ce, parent)
}
}
}
pub struct InterfaceEntity {
interface_name: CString,
method_entities: Vec<MethodEntity>,
constants: Vec<ConstantEntity>,
extends: Vec<Interface>,
bound_interface: Interface,
}
impl InterfaceEntity {
pub fn new(interface_name: impl Into<String>) -> Self {
Self {
interface_name: ensure_end_with_zero(interface_name.into()),
method_entities: Vec::new(),
constants: Vec::new(),
extends: Vec::new(),
bound_interface: Interface::null(),
}
}
pub fn add_method(&mut self, name: impl Into<String>) -> &mut MethodEntity {
let mut entity = MethodEntity::new(name, None, Visibility::Public);
entity.set_vis_abstract();
self.method_entities.push(entity);
self.method_entities.last_mut().unwrap()
}
pub fn add_static_method(&mut self, name: impl Into<String>) -> &mut MethodEntity {
let mut entity = MethodEntity::new(name, None, Visibility::Public);
entity.set_vis_abstract();
entity.set_vis_static();
self.method_entities.push(entity);
self.method_entities.last_mut().unwrap()
}
pub fn add_constant(&mut self, name: impl Into<String>, value: impl Into<Scalar>) {
let constant = ConstantEntity::new(name, value);
self.constants.push(constant);
}
pub fn extends(&mut self, interface: Interface) {
self.extends.push(interface);
}
#[allow(clippy::useless_conversion)]
pub(crate) unsafe fn init(&self) -> *mut zend_class_entry {
unsafe {
let class_ce = phper_init_class_entry_ex(
self.interface_name.as_ptr().cast(),
self.interface_name.as_bytes().len().try_into().unwrap(),
self.function_entries(),
Some(interface_init_handler),
null_mut(),
);
self.bound_interface.bind(class_ce);
for interface in &self.extends {
let interface_ce = interface.as_class_entry().as_ptr();
zend_class_implements(class_ce, 1, interface_ce);
}
for constant in &self.constants {
add_class_constant(class_ce, constant);
}
class_ce
}
}
unsafe fn function_entries(&self) -> *const zend_function_entry {
unsafe {
let mut methods = self
.method_entities
.iter()
.map(|method| FunctionEntry::from_method_entity(method))
.collect::<Vec<_>>();
methods.push(zeroed::<zend_function_entry>());
Box::into_raw(methods.into_boxed_slice()).cast()
}
}
#[inline]
pub fn bound_interface(&self) -> Interface {
self.bound_interface.clone()
}
}
unsafe extern "C" fn interface_init_handler(
class_ce: *mut zend_class_entry, _argument: *mut c_void,
) -> *mut zend_class_entry {
unsafe { zend_register_internal_interface(class_ce) }
}
pub struct ConstantEntity {
name: String,
value: Scalar,
}
impl ConstantEntity {
pub(crate) fn new(name: impl Into<String>, value: impl Into<Scalar>) -> Self {
Self {
name: name.into(),
value: value.into(),
}
}
}
pub(crate) struct PropertyEntity {
name: String,
visibility: RawVisibility,
value: Scalar,
}
impl PropertyEntity {
pub(crate) fn new(
name: impl Into<String>, visibility: Visibility, value: impl Into<Scalar>,
) -> Self {
Self {
name: name.into(),
visibility: visibility as RawVisibility,
value: value.into(),
}
}
#[inline]
pub(crate) fn set_vis_static(&mut self) -> &mut Self {
self.visibility |= ZEND_ACC_STATIC;
self
}
#[allow(clippy::useless_conversion)]
pub(crate) fn declare(&self, ce: *mut zend_class_entry) {
let name = self.name.as_ptr().cast();
let name_length = self.name.len().try_into().unwrap();
let access_type = self.visibility as i32;
unsafe {
match &self.value {
Scalar::Null => {
zend_declare_property_null(ce, name, name_length, access_type);
}
Scalar::Bool(b) => {
zend_declare_property_bool(ce, name, name_length, *b as zend_long, access_type);
}
Scalar::I64(i) => {
zend_declare_property_long(ce, name, name_length, *i, access_type);
}
Scalar::F64(f) => {
zend_declare_property_double(ce, name, name_length, *f, access_type);
}
Scalar::String(s) => {
zend_declare_property_stringl(
ce,
name,
name_length,
s.as_ptr().cast(),
s.len().try_into().unwrap(),
access_type,
);
}
Scalar::Bytes(b) => {
zend_declare_property_stringl(
ce,
name,
name_length,
b.as_ptr().cast(),
b.len().try_into().unwrap(),
access_type,
);
}
}
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum Visibility {
#[default]
Public = ZEND_ACC_PUBLIC,
Protected = ZEND_ACC_PROTECTED,
Private = ZEND_ACC_PRIVATE,
}
pub(crate) type RawVisibility = u32;
#[allow(clippy::useless_conversion)]
pub(crate) unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_object {
unsafe {
let real_ce = find_real_ce(ce).unwrap();
let state_object =
phper_zend_object_alloc(size_of::<StateObj<()>>().try_into().unwrap(), ce);
let state_object = StateObj::<()>::from_mut_ptr(state_object);
let mut func_ptr = (*real_ce).info.internal.builtin_functions;
while !(*func_ptr).fname.is_null() {
func_ptr = func_ptr.offset(1);
}
func_ptr = func_ptr.offset(1);
let state_constructor = func_ptr as *mut *const StateConstructor;
let state_constructor = state_constructor.read().as_ref().unwrap();
func_ptr = func_ptr.offset(1);
let has_state_cloner =
slice::from_raw_parts(func_ptr as *const u8, size_of::<*const StateCloner>())
!= [0u8; size_of::<*const StateCloner>()];
let object = state_object.as_mut_object().as_mut_ptr();
zend_object_std_init(object, ce);
object_properties_init(object, ce);
cfg_if::cfg_if! {
if #[cfg(any(
phper_major_version = "7",
all(
phper_major_version = "8",
any(
phper_minor_version = "0",
phper_minor_version = "1",
phper_minor_version = "2",
phper_minor_version = "3",
),
)
))] {
rebuild_object_properties(object);
} else {
rebuild_object_properties_internal(object);
}
}
let mut handlers = Box::new(std_object_handlers);
handlers.offset = StateObj::<()>::offset() as c_int;
handlers.free_obj = Some(free_object);
handlers.clone_obj = has_state_cloner.then_some(clone_object);
(*object).handlers = Box::into_raw(handlers);
let data = (state_constructor)();
*state_object.as_mut_any_state() = data;
object
}
}
#[cfg(phper_major_version = "8")]
unsafe extern "C" fn clone_object(object: *mut zend_object) -> *mut zend_object {
unsafe { clone_object_common(object) }
}
#[cfg(phper_major_version = "7")]
unsafe extern "C" fn clone_object(object: *mut zval) -> *mut zend_object {
unsafe {
let object = phper_z_obj_p(object);
clone_object_common(object)
}
}
#[allow(clippy::useless_conversion)]
unsafe fn clone_object_common(object: *mut zend_object) -> *mut zend_object {
unsafe {
let ce = (*object).ce;
let real_ce = find_real_ce(ce).unwrap();
let new_state_object =
phper_zend_object_alloc(size_of::<StateObj<()>>().try_into().unwrap(), ce);
let new_state_object = StateObj::<()>::from_mut_ptr(new_state_object);
let mut func_ptr = (*real_ce).info.internal.builtin_functions;
while !(*func_ptr).fname.is_null() {
func_ptr = func_ptr.offset(1);
}
func_ptr = func_ptr.offset(2);
let state_cloner = func_ptr as *mut *const StateCloner;
let state_cloner = state_cloner.read().as_ref().unwrap();
let new_object = new_state_object.as_mut_object().as_mut_ptr();
zend_object_std_init(new_object, ce);
object_properties_init(new_object, ce);
zend_objects_clone_members(new_object, object);
(*new_object).handlers = (*object).handlers;
let state_object = StateObj::<()>::from_mut_object_ptr(object);
let data = (state_cloner)(*state_object.as_mut_any_state());
*new_state_object.as_mut_any_state() = data;
new_object
}
}
pub(crate) unsafe fn add_class_constant(
class_ce: *mut _zend_class_entry, constant: &ConstantEntity,
) {
let name_ptr = constant.name.as_ptr() as *const c_char;
let name_len = constant.name.len();
unsafe {
match &constant.value {
Scalar::Null => {
zend_declare_class_constant_null(class_ce, name_ptr, name_len);
}
Scalar::Bool(b) => {
zend_declare_class_constant_bool(class_ce, name_ptr, name_len, *b as zend_bool);
}
Scalar::I64(i) => {
zend_declare_class_constant_long(class_ce, name_ptr, name_len, *i as zend_long);
}
Scalar::F64(f) => {
zend_declare_class_constant_double(class_ce, name_ptr, name_len, *f);
}
Scalar::String(s) => {
let s_ptr = s.as_ptr() as *mut u8;
zend_declare_class_constant_stringl(
class_ce,
name_ptr,
name_len,
s_ptr.cast(),
s.len(),
);
}
Scalar::Bytes(s) => {
let s_ptr = s.as_ptr() as *mut u8;
zend_declare_class_constant_stringl(
class_ce,
name_ptr,
name_len,
s_ptr.cast(),
s.len(),
);
}
}
}
}
unsafe extern "C" fn free_object(object: *mut zend_object) {
unsafe {
let state_object = StateObj::<()>::from_mut_object_ptr(object);
state_object.drop_state();
zend_object_std_dtor(object);
}
}
unsafe fn find_real_ce(mut ce: *mut zend_class_entry) -> Option<*mut zend_class_entry> {
unsafe {
let class_entities = global_module().class_entities();
while !ce.is_null() {
for entity in class_entities {
if ClassEntry::from_ptr(ce).get_name().to_c_str() == Ok(&entity.class_name) {
return Some(ce);
}
}
ce = phper_get_parent_class(ce);
}
None
}
}