@@ -14,7 +14,7 @@ use crate::{
14
14
arrays:: ZArr ,
15
15
errors:: { ClassNotFoundError , InitializeObjectError , Throwable } ,
16
16
functions:: { Function , FunctionEntry , Method , MethodEntity } ,
17
- objects:: { StateObj , StateObject , ZObj , ZObject } ,
17
+ objects:: { CStateObject , StateObj , StateObject , ZObj , ZObject } ,
18
18
strings:: ZStr ,
19
19
sys:: * ,
20
20
types:: Scalar ,
@@ -34,6 +34,7 @@ use std::{
34
34
os:: raw:: c_int,
35
35
ptr:: null_mut,
36
36
rc:: Rc ,
37
+ sync:: atomic:: { AtomicPtr , Ordering } ,
37
38
} ;
38
39
39
40
/// Predefined interface `Iterator`.
@@ -136,6 +137,9 @@ impl ClassEntry {
136
137
}
137
138
138
139
/// Create the object from class and call `__construct` with arguments.
140
+ ///
141
+ /// If the `__construct` is private, or protected and the called scope isn't
142
+ /// parent class, it will throw PHP Error.
139
143
pub fn new_object ( & self , arguments : impl AsMut < [ ZVal ] > ) -> crate :: Result < ZObject > {
140
144
let mut object = self . init_object ( ) ?;
141
145
object. call_construct ( arguments) ?;
@@ -199,6 +203,77 @@ fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
199
203
}
200
204
}
201
205
206
+ /// The [StateClass] holds [zend_class_entry](crate::sys::zend_class_entry) and
207
+ /// inner state, always as the static variable, and then be bind to
208
+ /// [ClassEntity].
209
+ ///
210
+ /// When the class registered (module initialized), the [StateClass] will be
211
+ /// initialized, so you can use the [StateClass] to new stateful object, etc.
212
+ ///
213
+ /// So, You shouldn't use [StateClass] in `module_init` stage, because it hasn't
214
+ /// initialized.
215
+ ///
216
+ /// # Examples
217
+ ///
218
+ /// ```rust
219
+ /// use phper::classes::{ClassEntity, StateClass};
220
+ ///
221
+ /// pub static FOO_CLASS: StateClass<FooState> = StateClass::new();
222
+ ///
223
+ /// #[derive(Default)]
224
+ /// pub struct FooState;
225
+ ///
226
+ /// fn make_foo_class() -> ClassEntity<FooState> {
227
+ /// let mut class = ClassEntity::new_with_default_state_constructor("Foo");
228
+ /// class.bind(&FOO_CLASS);
229
+ /// class
230
+ /// }
231
+ /// ```
232
+ #[ repr( transparent) ]
233
+ pub struct StateClass < T > {
234
+ inner : AtomicPtr < zend_class_entry > ,
235
+ _p : PhantomData < T > ,
236
+ }
237
+
238
+ impl < T > StateClass < T > {
239
+ /// Create empty [StateClass], with null
240
+ /// [zend_class_entry](crate::sys::zend_class_entry).
241
+ pub const fn new ( ) -> Self {
242
+ Self {
243
+ inner : AtomicPtr :: new ( null_mut ( ) ) ,
244
+ _p : PhantomData ,
245
+ }
246
+ }
247
+
248
+ fn bind ( & ' static self , ptr : * mut zend_class_entry ) {
249
+ self . inner . store ( ptr, Ordering :: Relaxed ) ;
250
+ }
251
+
252
+ /// Converts to class entry.
253
+ pub fn as_class_entry ( & ' static self ) -> & ' static ClassEntry {
254
+ unsafe { ClassEntry :: from_mut_ptr ( self . inner . load ( Ordering :: Relaxed ) ) }
255
+ }
256
+
257
+ /// Create the object from class and call `__construct` with arguments.
258
+ ///
259
+ /// If the `__construct` is private, or protected and the called scope isn't
260
+ /// parent class, it will throw PHP Error.
261
+ pub fn new_object (
262
+ & ' static self , arguments : impl AsMut < [ ZVal ] > ,
263
+ ) -> crate :: Result < StateObject < T > > {
264
+ self . as_class_entry ( )
265
+ . new_object ( arguments)
266
+ . map ( StateObject :: new)
267
+ }
268
+
269
+ /// Create the object from class, without calling `__construct`.
270
+ ///
271
+ /// **Be careful when `__construct` is necessary.**
272
+ pub fn init_object ( & ' static self ) -> crate :: Result < StateObject < T > > {
273
+ self . as_class_entry ( ) . init_object ( ) . map ( StateObject :: new)
274
+ }
275
+ }
276
+
202
277
pub ( crate ) type StateConstructor = dyn Fn ( ) -> Box < dyn Any > ;
203
278
204
279
/// Builder for registering class.
@@ -207,13 +282,14 @@ pub(crate) type StateConstructor = dyn Fn() -> Box<dyn Any>;
207
282
///
208
283
/// *It is a common practice for PHP extensions to use PHP objects to package
209
284
/// third-party resources.*
210
- pub struct ClassEntity < T > {
285
+ pub struct ClassEntity < T : ' static > {
211
286
class_name : CString ,
212
287
state_constructor : Rc < StateConstructor > ,
213
288
method_entities : Vec < MethodEntity > ,
214
289
property_entities : Vec < PropertyEntity > ,
215
290
parent : Option < Box < dyn Fn ( ) -> & ' static ClassEntry > > ,
216
291
interfaces : Vec < Box < dyn Fn ( ) -> & ' static ClassEntry > > ,
292
+ bind_class : Option < & ' static StateClass < T > > ,
217
293
_p : PhantomData < ( * mut ( ) , T ) > ,
218
294
}
219
295
@@ -245,6 +321,7 @@ impl<T: 'static> ClassEntity<T> {
245
321
property_entities : Vec :: new ( ) ,
246
322
parent : None ,
247
323
interfaces : Vec :: new ( ) ,
324
+ bind_class : None ,
248
325
_p : PhantomData ,
249
326
}
250
327
}
@@ -331,6 +408,14 @@ impl<T: 'static> ClassEntity<T> {
331
408
self . interfaces . push ( Box :: new ( interface) ) ;
332
409
}
333
410
411
+ /// Bind to static [StateClass].
412
+ ///
413
+ /// When the class registered, the [StateClass] will be initialized, so you
414
+ /// can use the [StateClass] to new stateful object, etc.
415
+ pub fn bind ( & mut self , cls : & ' static StateClass < T > ) {
416
+ self . bind_class = Some ( cls) ;
417
+ }
418
+
334
419
#[ allow( clippy:: useless_conversion) ]
335
420
pub ( crate ) unsafe fn init ( & self ) -> * mut zend_class_entry {
336
421
let parent: * mut zend_class_entry = self
@@ -348,6 +433,10 @@ impl<T: 'static> ClassEntity<T> {
348
433
parent. cast ( ) ,
349
434
) ;
350
435
436
+ if let Some ( bind_class) = self . bind_class {
437
+ bind_class. bind ( class_ce) ;
438
+ }
439
+
351
440
for interface in & self . interfaces {
352
441
let interface_ce = interface ( ) . as_ptr ( ) ;
353
442
zend_class_implements ( class_ce, 1 , interface_ce) ;
@@ -482,7 +571,7 @@ fn get_object_handlers() -> &'static zend_object_handlers {
482
571
static HANDLERS : OnceCell < zend_object_handlers > = OnceCell :: new ( ) ;
483
572
HANDLERS . get_or_init ( || unsafe {
484
573
let mut handlers = std_object_handlers;
485
- handlers. offset = StateObject :: offset ( ) as c_int ;
574
+ handlers. offset = CStateObject :: offset ( ) as c_int ;
486
575
handlers. free_obj = Some ( free_object) ;
487
576
handlers
488
577
} )
@@ -491,11 +580,11 @@ fn get_object_handlers() -> &'static zend_object_handlers {
491
580
#[ allow( clippy:: useless_conversion) ]
492
581
unsafe extern "C" fn create_object ( ce : * mut zend_class_entry ) -> * mut zend_object {
493
582
// Alloc more memory size to store state data.
494
- let state_object: * mut StateObject =
495
- phper_zend_object_alloc ( size_of :: < StateObject > ( ) . try_into ( ) . unwrap ( ) , ce) . cast ( ) ;
583
+ let state_object: * mut CStateObject =
584
+ phper_zend_object_alloc ( size_of :: < CStateObject > ( ) . try_into ( ) . unwrap ( ) , ce) . cast ( ) ;
496
585
497
586
// Common initialize process.
498
- let object = StateObject :: as_mut_object ( state_object) ;
587
+ let object = CStateObject :: as_mut_object ( state_object) ;
499
588
zend_object_std_init ( object, ce) ;
500
589
object_properties_init ( object, ce) ;
501
590
rebuild_object_properties ( object) ;
@@ -512,15 +601,15 @@ unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_objec
512
601
513
602
// Call the state constructor.
514
603
let data: Box < dyn Any > = ( state_constructor. as_ref ( ) . unwrap ( ) ) ( ) ;
515
- * StateObject :: as_mut_state ( state_object) = Box :: into_raw ( data) ;
604
+ * CStateObject :: as_mut_state ( state_object) = Box :: into_raw ( data) ;
516
605
517
606
object
518
607
}
519
608
520
609
unsafe extern "C" fn free_object ( object : * mut zend_object ) {
521
610
// Drop the state.
522
- let state_object = StateObject :: fetch_ptr ( object) ;
523
- StateObject :: drop_state ( state_object) ;
611
+ let state_object = CStateObject :: fetch_ptr ( object) ;
612
+ CStateObject :: drop_state ( state_object) ;
524
613
525
614
// Original destroy call.
526
615
zend_object_std_dtor ( object) ;
0 commit comments