• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 15788182852

20 Jun 2025 09:32PM UTC coverage: 22.034%. Remained the same
15788182852

Pull #460

github

web-flow
Merge d4fc01bb5 into 660f308c0
Pull Request #460: chore(clippy): flowing lifetimes warning and clippy::mut_from_ref

6 of 56 new or added lines in 13 files covered. (10.71%)

3 existing lines in 2 files now uncovered.

871 of 3953 relevant lines covered (22.03%)

2.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/types/class_object.rs
1
//! Represents an object in PHP. Allows for overriding the internal object used
2
//! by classes, allowing users to store Rust data inside a PHP object.
3

4
use std::{
5
    fmt::Debug,
6
    mem,
7
    ops::{Deref, DerefMut},
8
    os::raw::c_char,
9
    ptr::{self, NonNull},
10
};
11

12
use crate::{
13
    boxed::{ZBox, ZBoxable},
14
    class::RegisteredClass,
15
    convert::{FromZendObject, FromZendObjectMut, FromZval, FromZvalMut, IntoZval},
16
    error::{Error, Result},
17
    ffi::{
18
        ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init,
19
        zend_object, zend_object_std_init, zend_objects_clone_members,
20
    },
21
    flags::DataType,
22
    types::{ZendObject, Zval},
23
    zend::ClassEntry,
24
};
25

26
/// Representation of a Zend class object in memory.
27
#[repr(C)]
28
#[derive(Debug)]
29
pub struct ZendClassObject<T> {
30
    /// The object stored inside the class object.
31
    pub obj: Option<T>,
32
    /// The standard zend object.
33
    pub std: ZendObject,
34
}
35

36
impl<T: RegisteredClass> ZendClassObject<T> {
37
    /// Creates a new [`ZendClassObject`] of type `T`, where `T` is a
38
    /// [`RegisteredClass`] in PHP, storing the given value `val` inside the
39
    /// object.
40
    ///
41
    /// # Parameters
42
    ///
43
    /// * `val` - The value to store inside the object.
44
    ///
45
    /// # Panics
46
    ///
47
    /// Panics if memory was unable to be allocated for the new object.
48
    pub fn new(val: T) -> ZBox<Self> {
×
49
        // SAFETY: We are providing a value to initialize the object with.
50
        unsafe { Self::internal_new(Some(val), None) }
×
51
    }
52

53
    /// Creates a new [`ZendClassObject`] of type `T`, with an uninitialized
54
    /// internal object.
55
    ///
56
    /// # Safety
57
    ///
58
    /// As the object is uninitialized, the caller must ensure the following
59
    /// until the internal object is initialized:
60
    ///
61
    /// * The object is never dereferenced to `T`.
62
    /// * The [`Clone`] implementation is never called.
63
    /// * The [`Debug`] implementation is never called.
64
    ///
65
    /// If any of these conditions are not met while not initialized, the
66
    /// corresponding function will panic. Converting the object into its
67
    /// inner pointer with the [`into_raw`] function is valid, however.
68
    ///
69
    /// [`into_raw`]: #method.into_raw
70
    ///
71
    /// # Panics
72
    ///
73
    /// Panics if memory was unable to be allocated for the new object.
74
    pub unsafe fn new_uninit(ce: Option<&'static ClassEntry>) -> ZBox<Self> {
×
75
        Self::internal_new(None, ce)
×
76
    }
77

78
    /// Creates a new [`ZendObject`] of type `T`, storing the given (and
79
    /// potentially uninitialized) `val` inside the object.
80
    ///
81
    /// # Parameters
82
    ///
83
    /// * `val` - Value to store inside the object. See safety section.
84
    /// * `init` - Whether the given `val` was initialized.
85
    ///
86
    /// # Safety
87
    ///
88
    /// Providing an initialized variant of [`MaybeUninit<T>`] is safe.
89
    ///
90
    /// Providing an uninitialized variant of [`MaybeUninit<T>`] is unsafe. As
91
    /// the object is uninitialized, the caller must ensure the following
92
    /// until the internal object is initialized:
93
    ///
94
    /// * The object is never dereferenced to `T`.
95
    /// * The [`Clone`] implementation is never called.
96
    /// * The [`Debug`] implementation is never called.
97
    ///
98
    /// If any of these conditions are not met while not initialized, the
99
    /// corresponding function will panic. Converting the object into its
100
    /// inner with the [`into_raw`] function is valid, however. You can
101
    /// initialize the object with the [`initialize`] function.
102
    ///
103
    /// [`into_raw`]: #method.into_raw
104
    /// [`initialize`]: #method.initialize
105
    ///
106
    /// # Panics
107
    ///
108
    /// Panics if memory was unable to be allocated for the new object.
109
    unsafe fn internal_new(val: Option<T>, ce: Option<&'static ClassEntry>) -> ZBox<Self> {
×
110
        let size = mem::size_of::<ZendClassObject<T>>();
×
111
        let meta = T::get_metadata();
×
112
        let ce = ptr::from_ref(ce.unwrap_or_else(|| meta.ce())).cast_mut();
×
113
        let obj = ext_php_rs_zend_object_alloc(size as _, ce).cast::<ZendClassObject<T>>();
×
114
        let obj = obj
×
115
            .as_mut()
116
            .expect("Failed to allocate for new Zend object");
117

NEW
118
        zend_object_std_init(&raw mut obj.std, ce);
×
NEW
119
        object_properties_init(&raw mut obj.std, ce);
×
120

121
        // SAFETY: `obj` is non-null and well aligned as it is a reference.
122
        // As the data in `obj.obj` is uninitialized, we don't want to drop
123
        // the data, but directly override it.
NEW
124
        ptr::write(&raw mut obj.obj, val);
×
125

126
        obj.std.handlers = meta.handlers();
×
127
        ZBox::from_raw(obj)
×
128
    }
129

130
    /// Initializes the class object with the value `val`.
131
    ///
132
    /// # Parameters
133
    ///
134
    /// * `val` - The value to initialize the object with.
135
    ///
136
    /// # Returns
137
    ///
138
    /// Returns the old value in an [`Option`] if the object had already been
139
    /// initialized, [`None`] otherwise.
140
    pub fn initialize(&mut self, val: T) -> Option<T> {
×
141
        self.obj.replace(val)
×
142
    }
143

144
    /// Returns a mutable reference to the [`ZendClassObject`] of a given zend
145
    /// object `obj`. Returns [`None`] if the given object is not of the
146
    /// type `T`.
147
    ///
148
    /// # Parameters
149
    ///
150
    /// * `obj` - The zend object to get the [`ZendClassObject`] for.
151
    ///
152
    /// # Panics
153
    ///
154
    /// * If the std offset over/underflows `isize`.
155
    #[must_use]
156
    pub fn from_zend_obj(std: &zend_object) -> Option<&Self> {
×
157
        Some(Self::internal_from_zend_obj(std)?)
×
158
    }
159

160
    /// Returns a mutable reference to the [`ZendClassObject`] of a given zend
161
    /// object `obj`. Returns [`None`] if the given object is not of the
162
    /// type `T`.
163
    ///
164
    /// # Parameters
165
    ///
166
    /// * `obj` - The zend object to get the [`ZendClassObject`] for.
167
    ///
168
    /// # Panics
169
    ///
170
    /// * If the std offset over/underflows `isize`.
171
    #[allow(clippy::needless_pass_by_ref_mut)]
172
    pub fn from_zend_obj_mut(std: &mut zend_object) -> Option<&mut Self> {
×
173
        Self::internal_from_zend_obj(std)
×
174
    }
175

176
    fn internal_from_zend_obj(std: &zend_object) -> Option<&mut Self> {
×
177
        let std = ptr::from_ref(std).cast::<c_char>();
×
178
        let ptr = unsafe {
179
            let offset = isize::try_from(Self::std_offset()).expect("Offset overflow");
×
180
            let ptr = std.offset(0 - offset).cast::<Self>();
×
181
            ptr.cast_mut().as_mut()?
×
182
        };
183

184
        if ptr.std.instance_of(T::get_metadata().ce()) {
×
185
            Some(ptr)
×
186
        } else {
187
            None
×
188
        }
189
    }
190

191
    /// Returns a mutable reference to the underlying Zend object.
192
    pub fn get_mut_zend_obj(&mut self) -> &mut zend_object {
×
193
        &mut self.std
×
194
    }
195

196
    /// Returns the offset of the `std` property in the class object.
197
    pub(crate) fn std_offset() -> usize {
×
198
        unsafe {
199
            let null = NonNull::<Self>::dangling();
×
200
            let base = null.as_ref() as *const Self;
×
201
            let std = &raw const null.as_ref().std;
×
202

203
            (std as usize) - (base as usize)
×
204
        }
205
    }
206
}
207

208
impl<'a, T: RegisteredClass> FromZval<'a> for &'a ZendClassObject<T> {
209
    const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
210

211
    fn from_zval(zval: &'a Zval) -> Option<Self> {
×
212
        Self::from_zend_object(zval.object()?).ok()
×
213
    }
214
}
215

216
impl<'a, T: RegisteredClass> FromZendObject<'a> for &'a ZendClassObject<T> {
217
    fn from_zend_object(obj: &'a ZendObject) -> Result<Self> {
×
218
        // TODO(david): replace with better error
219
        ZendClassObject::from_zend_obj(obj).ok_or(Error::InvalidScope)
×
220
    }
221
}
222

223
impl<'a, T: RegisteredClass> FromZvalMut<'a> for &'a mut ZendClassObject<T> {
224
    const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
225

226
    fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
×
227
        Self::from_zend_object_mut(zval.object_mut()?).ok()
×
228
    }
229
}
230

231
impl<'a, T: RegisteredClass> FromZendObjectMut<'a> for &'a mut ZendClassObject<T> {
232
    fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result<Self> {
×
233
        ZendClassObject::from_zend_obj_mut(obj).ok_or(Error::InvalidScope)
×
234
    }
235
}
236

237
unsafe impl<T: RegisteredClass> ZBoxable for ZendClassObject<T> {
238
    fn free(&mut self) {
×
239
        // SAFETY: All constructors guarantee that `self` contains a valid pointer.
240
        // Further, all constructors guarantee that the `std` field of
241
        // `ZendClassObject` will be initialized.
NEW
242
        unsafe { ext_php_rs_zend_object_release(&raw mut self.std) }
×
243
    }
244
}
245

246
impl<T> Deref for ZendClassObject<T> {
247
    type Target = T;
248

249
    fn deref(&self) -> &Self::Target {
×
250
        self.obj
×
251
            .as_ref()
252
            .expect("Attempted to access uninitialized class object")
253
    }
254
}
255

256
impl<T> DerefMut for ZendClassObject<T> {
257
    fn deref_mut(&mut self) -> &mut Self::Target {
×
258
        self.obj
×
259
            .as_mut()
260
            .expect("Attempted to access uninitialized class object")
261
    }
262
}
263

264
impl<T: RegisteredClass + Default> Default for ZBox<ZendClassObject<T>> {
265
    #[inline]
266
    fn default() -> Self {
×
267
        ZendClassObject::new(T::default())
×
268
    }
269
}
270

271
impl<T: RegisteredClass + Clone> Clone for ZBox<ZendClassObject<T>> {
272
    fn clone(&self) -> Self {
×
273
        // SAFETY: All constructors of `NewClassObject` guarantee that it will contain a
274
        // valid pointer. The constructor also guarantees that the internal
275
        // `ZendClassObject` pointer will contain a valid, initialized `obj`,
276
        // therefore we can dereference both safely.
277
        unsafe {
278
            let mut new = ZendClassObject::new((***self).clone());
×
NEW
279
            zend_objects_clone_members(&raw mut new.std, (&raw const self.std).cast_mut());
×
280
            new
×
281
        }
282
    }
283
}
284

285
impl<T: RegisteredClass> IntoZval for ZBox<ZendClassObject<T>> {
286
    const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
287
    const NULLABLE: bool = false;
288

289
    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
×
290
        let obj = self.into_raw();
×
291
        zv.set_object(&mut obj.std);
×
292
        Ok(())
×
293
    }
294
}
295

296
impl<T: RegisteredClass> IntoZval for &mut ZendClassObject<T> {
297
    const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
298
    const NULLABLE: bool = false;
299

300
    #[inline]
301
    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
×
302
        zv.set_object(&mut self.std);
×
303
        Ok(())
×
304
    }
305
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc