|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2017-12-05 14:08 UTC] kripper at imatronix dot com
Description:
------------
Setting a unused reference to a cloned object property, prevents the object to be cloned.
This makes no sense and is intuitive.
Test script:
---------------
$orig = new stdClass();
$orig->test = 'original';
$ptr = & $orig->test; // <--- Setting an unused reference changes the behaviour
$copy = clone $orig;
$copy->test = "modified";
die("Orig is: {$orig->test}"); // Gives 'modified' instead of 'original'
Expected result:
----------------
$orig->test should give 'original' since we cloned the object.
A cloned object should be a cloned object, no matter of unused references.
Actual result:
--------------
Setting a reference makes "$copy = clone $original" to behave just like "$copy = $original" (a reference).
Not setting or unsetting the refernce, fixes the behavior.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Tue Dec 16 06:00:02 2025 UTC |
<?php // We used to have objects with multilevel array data... $base = new stdClass(); $base->data = array( 'nodes' => array( 'subNodes' => array( 'subSubNodes' => '...' ) ) ); // And we used references to write cleaner code: $ref = & $base->data['nodes']['subNodes']['subSubNodes']; // ChangeData($ref); // Instead of: // ChangeData($base->data['nodes']['subNodes']); // This has a very big impact on legibility, if not performance. // But since PHP 7.1, keeping references prevents 'clone' to work properly // because the cloned object properties are treated as references to the // original object properties. $clone = clone $base; // So, when changing the cloned object... $clone->data['nodes']['subNodes'] = 'Changed by clone'; // ...the original object is also modified (result is: 'Changed by clone'). die("Base object has property: " . $base->data['nodes']['subNodes']); It seems like references are not really references anymore, but variable aliases, which cannot be usted together with clone(). Actually, we feel references aren't safe nor usefull anymore, but for 'out arguments' when calling functions (which can better be done by returning arrays anyway). Of course, we can unset the reference before doing clone, but it's not feasible cosidering we can accidently call clone from a subfunction while the reference is still alive. Is there any solution or workaround? If not, we probably have a language design problem here that will make noise to serious developers until PHP introduces pointers or references that can be usted together with clone.Thanks Damian, I want to write legible code like this: $ref = & $base->data['nodes']['subNodes']['subSubNodes']; foreach($res as &val) { if(!$res['sub']) $res['sub'] = 'something'; CloneObjectsWithoutUnsettingRefs($res); if($res['other'] = 'ok') $res['ok'] = true; } Instead of: foreach($base->data['nodes']['subNodes']['subSubNodes'] as &val) { if(!$base->data['nodes']['subNodes']['subSubNodes']['sub']) $base->data['nodes']['subNodes']['subSubNodes']['sub'] = 'something'; CloneObjectsWithoutUnsettingRefs($base->data['nodes']['subNodes']['subSubNodes']); if($base->data['nodes']['subNodes']['subSubNodes']['other'] = 'ok') $base->data['nodes']['subNodes']['subSubNodes']['ok'] = true; } What is the best approach for this? Using references causes problems when cloning objects.That code is a bit weird so I'm going to make what I think are some corrections: $ref = $base->data['nodes']['subNodes']['subSubNodes']; foreach($ref as $key => $res) { if(!$res['sub']) $res['sub'] = 'something'; $res = CloneObjectsWithoutUnsettingRefs($res); if($res['other'] == 'ok') $res['ok'] = true; $ref[$key] = $res; } $base->data['nodes']['subNodes']['subSubNodes'] = $ref; Point is you can modify the copies and then overwrite the originals.