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()
    }
}