ext_php_rs/zend/class.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! Builder and objects for creating classes in the PHP world.
use crate::ffi::instanceof_function_slow;
use crate::types::{ZendIterator, Zval};
use crate::{
boxed::ZBox,
ffi::zend_class_entry,
flags::ClassFlags,
types::{ZendObject, ZendStr},
zend::ExecutorGlobals,
};
use std::{convert::TryInto, fmt::Debug, ops::DerefMut};
/// A PHP class entry.
///
/// Represents a class registered with the PHP interpreter.
pub type ClassEntry = zend_class_entry;
impl ClassEntry {
/// Attempts to find a reference to a class in the global class table.
///
/// Returns a reference to the class if found, or [`None`] if the class
/// could not be found or the class table has not been initialized.
pub fn try_find(name: &str) -> Option<&'static Self> {
ExecutorGlobals::get().class_table()?;
let mut name = ZendStr::new(name, false);
unsafe {
crate::ffi::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0).as_ref()
}
}
/// Creates a new [`ZendObject`], returned inside an [`ZBox<ZendObject>`]
/// wrapper.
///
/// # Panics
///
/// Panics when allocating memory for the new object fails.
#[allow(clippy::new_ret_no_self)]
pub fn new(&self) -> ZBox<ZendObject> {
ZendObject::new(self)
}
/// Returns the class flags.
pub fn flags(&self) -> ClassFlags {
ClassFlags::from_bits_truncate(self.ce_flags)
}
/// Returns `true` if the class entry is an interface, and `false`
/// otherwise.
pub fn is_interface(&self) -> bool {
self.flags().contains(ClassFlags::Interface)
}
/// Checks if the class is an instance of another class or interface.
///
/// # Parameters
///
/// * `other` - The inherited class entry to check.
pub fn instance_of(&self, other: &ClassEntry) -> bool {
if self == other {
return true;
}
unsafe { instanceof_function_slow(self as _, other as _) }
}
/// Returns an iterator of all the interfaces that the class implements.
///
/// Returns [`None`] if the interfaces have not been resolved on the
/// class.
pub fn interfaces(&self) -> Option<impl Iterator<Item = &ClassEntry>> {
self.flags()
.contains(ClassFlags::ResolvedInterfaces)
.then(|| unsafe {
(0..self.num_interfaces)
.map(move |i| *self.__bindgen_anon_3.interfaces.offset(i as _))
.filter_map(|ptr| ptr.as_ref())
})
}
/// Returns the parent of the class.
///
/// If the parent of the class has not been resolved, it attempts to find
/// the parent by name. Returns [`None`] if the parent was not resolved
/// and the parent was not able to be found by name.
pub fn parent(&self) -> Option<&Self> {
if self.flags().contains(ClassFlags::ResolvedParent) {
unsafe { self.__bindgen_anon_1.parent.as_ref() }
} else {
let name = unsafe { self.__bindgen_anon_1.parent_name.as_ref()? };
Self::try_find(name.as_str().ok()?)
}
}
/// Returns the iterator for the class for a specific instance
///
/// Returns [`None`] if there is no associated iterator for the class.
pub fn get_iterator<'a>(&self, zval: &'a Zval, by_ref: bool) -> Option<&'a mut ZendIterator> {
let ptr: *const Self = self;
let zval_ptr: *const Zval = zval;
let iterator = unsafe {
(*ptr).get_iterator?(
ptr as *mut ClassEntry,
zval_ptr as *mut Zval,
if by_ref { 1 } else { 0 },
)
};
unsafe { iterator.as_mut() }
}
pub fn name(&self) -> Option<&str> {
unsafe { self.name.as_ref().and_then(|s| s.as_str().ok()) }
}
}
impl PartialEq for ClassEntry {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Debug for ClassEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name: String = unsafe { self.name.as_ref() }
.and_then(|s| s.try_into().ok())
.ok_or(std::fmt::Error)?;
f.debug_struct("ClassEntry")
.field("name", &name)
.field("flags", &self.flags())
.field("is_interface", &self.is_interface())
.field(
"interfaces",
&self.interfaces().map(|iter| iter.collect::<Vec<_>>()),
)
.field("parent", &self.parent())
.finish()
}
}