Bug #75632 [Nab]: Wrong behavior of clone and references

From: Date: Tue, 05 Dec 2017 19:13:10 +0000
Subject: Bug #75632 [Nab]: Wrong behavior of clone and references
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-212949@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=75632&edit=1 ID: 75632 Updated by: requinix@php.net Reported by: kripper at imatronix dot com Summary: Wrong behavior of clone and references Status: Not a bug Type: Bug Package: Scripting Engine problem Operating System: All PHP Version: 7.1.12 Block user comment: N Private report: N New Comment: > // But since PHP 7.1, ... > It seems like references are not really references anymore, They've always worked that way. https://3v4l.org/70ZIS > 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? Unsetting the reference before the function call is the answer. Not using a reference in the first place is an even better answer. Using a reference for legibility? I don't see how ChangeData($ref); is more understandable than ChangeData($base->data['nodes']['subNodes']); Using a reference for performance? Don't. That's the #1 way to abuse references. PHP is already efficient with variables and it already has copy-on-write functionality so don't fight it by trying to be clever. If you want to propose additions like pointers or changes like... I don't know what you want to change with references... then check out PHP's RFC process. https://wiki.php.net/rfc/howto Previous Comments: ------------------------------------------------------------------------ [2017-12-05 18:51:12] kripper at imatronix dot com <?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. ------------------------------------------------------------------------ [2017-12-05 14:55:16] requinix@php.net Thank you for taking the time to write to us, but this is not a bug. Please double-check the documentation available at http://www.php.net/manual/ and the instructions on how to report a bug at http://bugs.php.net/how-to-report.php References are not pointers: $ptr does not "point" to $orig->test, rather they both use the same underlying value stored in memory. Also, creating a reference forces both sides of the assignment to be references - otherwise the system wouldn't work. http://php.net/manual/en/language.references.php If you unset($ptr) before the clone then $orig->test will become the only reference to its value left and so automatically revert to a normal unreferenced value. https://3v4l.org/c6epk ------------------------------------------------------------------------ [2017-12-05 14:08:13] 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. ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=75632&edit=1

« previous php.bugs (#212949) next »