@@ -21,7 +21,6 @@ use crate::{
21
21
utils:: ensure_end_with_zero,
22
22
values:: ZVal ,
23
23
} ;
24
- use once_cell:: sync:: OnceCell ;
25
24
use std:: {
26
25
any:: Any ,
27
26
borrow:: ToOwned ,
@@ -33,6 +32,7 @@ use std::{
33
32
os:: raw:: c_int,
34
33
ptr:: null_mut,
35
34
rc:: Rc ,
35
+ slice,
36
36
sync:: atomic:: { AtomicPtr , Ordering } ,
37
37
} ;
38
38
@@ -282,6 +282,8 @@ impl<T> StateClass<T> {
282
282
283
283
pub ( crate ) type StateConstructor = dyn Fn ( ) -> * mut dyn Any ;
284
284
285
+ pub ( crate ) type StateCloner = dyn Fn ( * const dyn Any ) -> * mut dyn Any ;
286
+
285
287
/// Builder for registering class.
286
288
///
287
289
/// `<T>` means the type of holding state.
@@ -296,6 +298,7 @@ pub struct ClassEntity<T: 'static> {
296
298
parent : Option < Box < dyn Fn ( ) -> & ' static ClassEntry > > ,
297
299
interfaces : Vec < Box < dyn Fn ( ) -> & ' static ClassEntry > > ,
298
300
bind_class : Option < & ' static StateClass < T > > ,
301
+ state_cloner : Option < Rc < StateCloner > > ,
299
302
_p : PhantomData < ( * mut ( ) , T ) > ,
300
303
}
301
304
@@ -332,6 +335,7 @@ impl<T: 'static> ClassEntity<T> {
332
335
parent : None ,
333
336
interfaces : Vec :: new ( ) ,
334
337
bind_class : None ,
338
+ state_cloner : None ,
335
339
_p : PhantomData ,
336
340
}
337
341
}
@@ -426,6 +430,47 @@ impl<T: 'static> ClassEntity<T> {
426
430
self . bind_class = Some ( cls) ;
427
431
}
428
432
433
+ /// Add the state clone function, called when cloning PHP object.
434
+ ///
435
+ /// By default, the object registered by `phper` is uncloneable, if you
436
+ /// clone the object in PHP like this:
437
+ ///
438
+ /// ```php
439
+ /// $foo = new Foo();
440
+ /// $foo2 = clone $foo;
441
+ /// ```
442
+ ///
443
+ /// Will throw the Error: `Uncaught Error: Trying to clone an uncloneable
444
+ /// object of class Foo`.
445
+ ///
446
+ /// And then, if you want the object to be cloneable, you should register
447
+ /// the state clone method for the class.
448
+ ///
449
+ /// # Examples
450
+ ///
451
+ /// ```
452
+ /// use phper::classes::ClassEntity;
453
+ ///
454
+ /// fn make_foo_class() -> ClassEntity<i64> {
455
+ /// let mut class = ClassEntity::new_with_state_constructor("Foo", || 123456);
456
+ /// class.state_cloner(Clone::clone);
457
+ /// class
458
+ /// }
459
+ /// ```
460
+ pub fn state_cloner ( & mut self , clone_fn : impl Fn ( & T ) -> T + ' static ) {
461
+ self . state_cloner = Some ( Rc :: new ( move |src| {
462
+ let src = unsafe {
463
+ src. as_ref ( )
464
+ . unwrap ( )
465
+ . downcast_ref :: < T > ( )
466
+ . expect ( "cast Any to T failed" )
467
+ } ;
468
+ let dest = clone_fn ( src) ;
469
+ let boxed = Box :: new ( dest) as Box < dyn Any > ;
470
+ Box :: into_raw ( boxed)
471
+ } ) ) ;
472
+ }
473
+
429
474
#[ allow( clippy:: useless_conversion) ]
430
475
pub ( crate ) unsafe fn init ( & self ) -> * mut zend_class_entry {
431
476
let parent: * mut zend_class_entry = self
@@ -464,7 +509,6 @@ impl<T: 'static> ClassEntity<T> {
464
509
}
465
510
466
511
unsafe fn function_entries ( & self ) -> * const zend_function_entry {
467
- let last_entry = self . take_state_constructor_into_function_entry ( ) ;
468
512
let mut methods = self
469
513
. method_entities
470
514
. iter ( )
@@ -473,8 +517,11 @@ impl<T: 'static> ClassEntity<T> {
473
517
474
518
methods. push ( zeroed :: < zend_function_entry > ( ) ) ;
475
519
476
- // Store the state constructor pointer to zend_class_entry
477
- methods. push ( last_entry) ;
520
+ // Store the state constructor pointer to zend_class_entry.
521
+ methods. push ( self . take_state_constructor_into_function_entry ( ) ) ;
522
+
523
+ // Store the state cloner pointer to zend_class_entry.
524
+ methods. push ( self . take_state_cloner_into_function_entry ( ) ) ;
478
525
479
526
Box :: into_raw ( methods. into_boxed_slice ( ) ) . cast ( )
480
527
}
@@ -486,6 +533,16 @@ impl<T: 'static> ClassEntity<T> {
486
533
ptr. write ( state_constructor) ;
487
534
entry
488
535
}
536
+
537
+ unsafe fn take_state_cloner_into_function_entry ( & self ) -> zend_function_entry {
538
+ let mut entry = zeroed :: < zend_function_entry > ( ) ;
539
+ let ptr = & mut entry as * mut _ as * mut * const StateCloner ;
540
+ if let Some ( state_cloner) = & self . state_cloner {
541
+ let state_constructor = Rc :: into_raw ( state_cloner. clone ( ) ) ;
542
+ ptr. write ( state_constructor) ;
543
+ }
544
+ entry
545
+ }
489
546
}
490
547
491
548
unsafe extern "C" fn class_init_handler (
@@ -577,45 +634,97 @@ pub enum Visibility {
577
634
Private = ZEND_ACC_PRIVATE ,
578
635
}
579
636
580
- fn get_object_handlers ( ) -> & ' static zend_object_handlers {
581
- static HANDLERS : OnceCell < zend_object_handlers > = OnceCell :: new ( ) ;
582
- HANDLERS . get_or_init ( || unsafe {
583
- let mut handlers = std_object_handlers;
584
- handlers. offset = StateObj :: < ( ) > :: offset ( ) as c_int ;
585
- handlers. free_obj = Some ( free_object) ;
586
- handlers
587
- } )
588
- }
589
-
590
637
#[ allow( clippy:: useless_conversion) ]
591
638
unsafe extern "C" fn create_object ( ce : * mut zend_class_entry ) -> * mut zend_object {
592
639
// Alloc more memory size to store state data.
593
640
let state_object = phper_zend_object_alloc ( size_of :: < StateObj < ( ) > > ( ) . try_into ( ) . unwrap ( ) , ce) ;
594
641
let state_object = StateObj :: < ( ) > :: from_mut_ptr ( state_object) ;
595
642
596
- // Common initialize process.
597
- let object = state_object. as_mut_object ( ) . as_mut_ptr ( ) ;
598
- zend_object_std_init ( object, ce) ;
599
- object_properties_init ( object, ce) ;
600
- rebuild_object_properties ( object) ;
601
- ( * object) . handlers = get_object_handlers ( ) ;
602
-
603
- // Get state constructor.
643
+ // Find the hack elements hidden behind null builtin_function.
604
644
let mut func_ptr = ( * ce) . info . internal . builtin_functions ;
605
645
while !( * func_ptr) . fname . is_null ( ) {
606
646
func_ptr = func_ptr. offset ( 1 ) ;
607
647
}
648
+
649
+ // Get state constructor.
608
650
func_ptr = func_ptr. offset ( 1 ) ;
609
651
let state_constructor = func_ptr as * mut * const StateConstructor ;
610
- let state_constructor = state_constructor. read ( ) ;
652
+ let state_constructor = state_constructor. read ( ) . as_ref ( ) . unwrap ( ) ;
653
+
654
+ // Get state cloner.
655
+ func_ptr = func_ptr. offset ( 1 ) ;
656
+ let has_state_cloner =
657
+ slice:: from_raw_parts ( func_ptr as * const u8 , size_of :: < * const StateCloner > ( ) )
658
+ != [ 0u8 ; size_of :: < * const StateCloner > ( ) ] ;
659
+
660
+ // Common initialize process.
661
+ let object = state_object. as_mut_object ( ) . as_mut_ptr ( ) ;
662
+ zend_object_std_init ( object, ce) ;
663
+ object_properties_init ( object, ce) ;
664
+ rebuild_object_properties ( object) ;
665
+
666
+ // Set handlers
667
+ let mut handlers = Box :: new ( std_object_handlers) ;
668
+ handlers. offset = StateObj :: < ( ) > :: offset ( ) as c_int ;
669
+ handlers. free_obj = Some ( free_object) ;
670
+ handlers. clone_obj = has_state_cloner. then_some ( clone_object) ;
671
+ ( * object) . handlers = Box :: into_raw ( handlers) ;
611
672
612
673
// Call the state constructor and store the state.
613
- let data = ( state_constructor. as_ref ( ) . unwrap ( ) ) ( ) ;
674
+ let data = ( state_constructor) ( ) ;
614
675
* state_object. as_mut_any_state ( ) = data;
615
676
616
677
object
617
678
}
618
679
680
+ #[ cfg( phper_major_version = "8" ) ]
681
+ unsafe extern "C" fn clone_object ( object : * mut zend_object ) -> * mut zend_object {
682
+ clone_object_common ( object)
683
+ }
684
+
685
+ #[ cfg( phper_major_version = "7" ) ]
686
+ unsafe extern "C" fn clone_object ( object : * mut zval ) -> * mut zend_object {
687
+ let object = phper_z_obj_p ( object) ;
688
+ clone_object_common ( object)
689
+ }
690
+
691
+ #[ allow( clippy:: useless_conversion) ]
692
+ unsafe fn clone_object_common ( object : * mut zend_object ) -> * mut zend_object {
693
+ let ce = ( * object) . ce ;
694
+
695
+ // Alloc more memory size to store state data.
696
+ let new_state_object =
697
+ phper_zend_object_alloc ( size_of :: < StateObj < ( ) > > ( ) . try_into ( ) . unwrap ( ) , ce) ;
698
+ let new_state_object = StateObj :: < ( ) > :: from_mut_ptr ( new_state_object) ;
699
+
700
+ // Find the hack elements hidden behind null builtin_function.
701
+ let mut func_ptr = ( * ( * object) . ce ) . info . internal . builtin_functions ;
702
+ while !( * func_ptr) . fname . is_null ( ) {
703
+ func_ptr = func_ptr. offset ( 1 ) ;
704
+ }
705
+
706
+ // Get state cloner.
707
+ func_ptr = func_ptr. offset ( 2 ) ;
708
+ let state_cloner = func_ptr as * mut * const StateCloner ;
709
+ let state_cloner = state_cloner. read ( ) . as_ref ( ) . unwrap ( ) ;
710
+
711
+ // Initialize and clone members
712
+ let new_object = new_state_object. as_mut_object ( ) . as_mut_ptr ( ) ;
713
+ zend_object_std_init ( new_object, ce) ;
714
+ object_properties_init ( new_object, ce) ;
715
+ zend_objects_clone_members ( new_object, object) ;
716
+
717
+ // Set handlers
718
+ ( * new_object) . handlers = ( * object) . handlers ;
719
+
720
+ // Call the state cloner and store the state.
721
+ let state_object = StateObj :: < ( ) > :: from_mut_object_ptr ( object) ;
722
+ let data = ( state_cloner) ( * state_object. as_mut_any_state ( ) ) ;
723
+ * new_state_object. as_mut_any_state ( ) = data;
724
+
725
+ new_object
726
+ }
727
+
619
728
unsafe extern "C" fn free_object ( object : * mut zend_object ) {
620
729
let state_object = StateObj :: < ( ) > :: from_mut_object_ptr ( object) ;
621
730
0 commit comments