@@ -27,12 +27,12 @@ protected function doClone(mixed $var): array
2727 $ len = 1 ; // Length of $queue
2828 $ pos = 0 ; // Number of cloned items past the minimum depth
2929 $ refsCounter = 0 ; // Hard references counter
30- $ queue = [[$ var ]]; // This breadth-first queue is the return value
31- $ hardRefs = []; // Map of original zval ids to stub objects
32- $ objRefs = []; // Map of original object handles to their stub object counterpart
33- $ objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning
34- $ resRefs = []; // Map of original resource handles to their stub object counterpart
35- $ values = []; // Map of stub objects' ids to original values
30+ $ queue = [[$ var ]]; // This breadth-first queue is the return value
31+ $ hardRefs = []; // Map of original zval ids to stub objects
32+ $ objRefs = []; // Map of original object handles to their stub object counterpart
33+ $ objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning
34+ $ resRefs = []; // Map of original resource handles to their stub object counterpart
35+ $ values = []; // Map of stub objects' ids to original values
3636 $ maxItems = $ this ->maxItems ;
3737 $ maxString = $ this ->maxString ;
3838 $ minDepth = $ this ->minDepth ;
@@ -63,25 +63,23 @@ protected function doClone(mixed $var): array
6363 foreach ($ vals as $ k => $ v ) {
6464 // $v is the original value or a stub object in case of hard references
6565
66- $ zvalIsRef = null !== \ReflectionReference::fromArrayElement ($ vals , $ k );
66+ $ zvalRef = ( $ r = \ReflectionReference::fromArrayElement ($ vals , $ k )) ? $ r -> getId () : null ;
6767
68- if ($ zvalIsRef ) {
68+ if ($ zvalRef ) {
6969 $ vals [$ k ] = &$ stub ; // Break hard references to make $queue completely
7070 unset($ stub ); // independent from the original structure
71- if ($ v instanceof Stub && isset ( $ hardRefs [spl_object_id ( $ v )]) ) {
72- $ vals [ $ k ] = $ refs [$ k ] = $ v ;
71+ if (null !== $ vals [ $ k ] = $ hardRefs [$ zvalRef ] ?? null ) {
72+ $ v = $ vals [$ k ];
7373 if ($ v ->value instanceof Stub && (Stub::TYPE_OBJECT === $ v ->value ->type || Stub::TYPE_RESOURCE === $ v ->value ->type )) {
7474 ++$ v ->value ->refCount ;
7575 }
7676 ++$ v ->refCount ;
7777 continue ;
7878 }
79- $ refs [$ k ] = $ vals [$ k ] = new Stub ();
80- $ refs [$ k ]->value = $ v ;
81- $ h = spl_object_id ($ refs [$ k ]);
82- $ hardRefs [$ h ] = &$ refs [$ k ];
83- $ values [$ h ] = $ v ;
79+ $ vals [$ k ] = new Stub ();
80+ $ vals [$ k ]->value = $ v ;
8481 $ vals [$ k ]->handle = ++$ refsCounter ;
82+ $ hardRefs [$ zvalRef ] = $ vals [$ k ];
8583 }
8684 // Create $stub when the original value $v can not be used directly
8785 // If $v is a nested structure, put that structure in array $a
@@ -122,39 +120,46 @@ protected function doClone(mixed $var): array
122120 continue 2 ;
123121 }
124122 $ stub = $ arrayStub ;
123+
124+ if (\PHP_VERSION_ID >= 80100 ) {
125+ $ stub ->class = array_is_list ($ v ) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC ;
126+ $ a = $ v ;
127+ break ;
128+ }
129+
125130 $ stub ->class = Stub::ARRAY_INDEXED ;
126131
127132 $ j = -1 ;
128133 foreach ($ v as $ gk => $ gv ) {
129134 if ($ gk !== ++$ j ) {
130135 $ stub ->class = Stub::ARRAY_ASSOC ;
136+ $ a = $ v ;
137+ $ a [$ gid ] = true ;
131138 break ;
132139 }
133140 }
134- $ a = $ v ;
135-
136- if (Stub::ARRAY_ASSOC === $ stub ->class ) {
137- // Copies of $GLOBALS have very strange behavior,
138- // let's detect them with some black magic
139- if (\PHP_VERSION_ID < 80100 && ($ a [$ gid ] = true ) && isset ($ v [$ gid ])) {
140- unset($ v [$ gid ]);
141- $ a = [];
142- foreach ($ v as $ gk => &$ gv ) {
143- if ($ v === $ gv ) {
144- unset($ v );
145- $ v = new Stub ();
146- $ v ->value = [$ v ->cut = \count ($ gv ), Stub::TYPE_ARRAY => 0 ];
147- $ v ->handle = -1 ;
148- $ gv = &$ hardRefs [spl_object_id ($ v )];
149- $ gv = $ v ;
150- }
151141
152- $ a [$ gk ] = &$ gv ;
142+ // Copies of $GLOBALS have very strange behavior,
143+ // let's detect them with some black magic
144+ if (isset ($ v [$ gid ])) {
145+ unset($ v [$ gid ]);
146+ $ a = [];
147+ foreach ($ v as $ gk => &$ gv ) {
148+ if ($ v === $ gv && !isset ($ hardRefs [\ReflectionReference::fromArrayElement ($ v , $ gk )->getId ()])) {
149+ unset($ v );
150+ $ v = new Stub ();
151+ $ v ->value = [$ v ->cut = \count ($ gv ), Stub::TYPE_ARRAY => 0 ];
152+ $ v ->handle = -1 ;
153+ $ gv = &$ a [$ gk ];
154+ $ hardRefs [\ReflectionReference::fromArrayElement ($ a , $ gk )->getId ()] = &$ gv ;
155+ $ gv = $ v ;
153156 }
154- unset($ gv );
155- } else {
156- $ a = $ v ;
157+
158+ $ a [$ gk ] = &$ gv ;
157159 }
160+ unset($ gv );
161+ } else {
162+ $ a = $ v ;
158163 }
159164 break ;
160165
@@ -244,10 +249,10 @@ protected function doClone(mixed $var): array
244249 }
245250 }
246251
247- if ($ zvalIsRef ) {
248- $ refs [$ k ]->value = $ stub ;
249- } else {
252+ if (!$ zvalRef ) {
250253 $ vals [$ k ] = $ stub ;
254+ } else {
255+ $ hardRefs [$ zvalRef ]->value = $ stub ;
251256 }
252257 }
253258
0 commit comments