|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2013-11-07 02:54 UTC] bawolff+wn at gmail dot com
Description: ------------ The problem is easiest understood by looking at the test script. Basically, if you clone an object in a function that as one of its arguments (or one of its calling functions) has a reference to a member variable of this object, the clone of the object is not correct for the field that the reference is to. The fields of the two objects behave as if they are references to each other (To clarify I do not mean fields point to the same object as is expected, I mean the field itself is a reference to the other field.). If you assign something to that member variable, the change is reflected in both the cloned object and the original, where it should only be reflected in the object that the assignment was for. Tested on snapshot php5.5-201311070030 Also tested on earlier versions like 5.3.3-7 Originally discovered while investigating a bug in MediaWiki: https://bugzilla.wikimedia.org/show_bug.cgi?id=56226 Test script: --------------- <?php $originalObj = new A; class A { var $foo = 'default value'; } // Important note: If you remove the &, the bug disappears. function changeFooWithUnusedReference( &$unusedReferenceToFoo, $newValue ) { global $originalObj; $newObj = clone $originalObj; echo $originalObj->foo . "\n"; echo $newObj->foo . "\n"; $newObj->foo = $newValue; echo $originalObj->foo . "\n"; echo $newObj->foo . "\n"; } changeFooWithUnusedReference( $originalObj->foo, 'Some other value' ); Expected result: ---------------- I expect the following output: default value default value default value Some other value Actual result: -------------- Following output: default value default value Some other value Some other value PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 21:00:01 2025 UTC |
I think I am encountering this bug, in a slightly different arrangement, and in PHP7.0.27 (the most up-to-date Debian release of PHP), so if it is the same, as seems likely, it's a rather nasty core bug that's been around for a long time. I also verified that @bawolff's example still fails in 7.0.27. I have while ($q->f($obj) > 0) { $list[$obj->field] = clone $obj; } where f sets fields in $obj. The list ends up with all 'field' members being the same as the final one. I don't think there is any PHP I could write in f that would do this accidentally as it doesn't have access to $list and clone should make the objects be distinct from each other. If I change it to: while ($q->f($obj) > 0) { $k = obj->field; $list[$k] = clone $obj; } it works. Whether this is because the code generated has been perturbed slightly, or it really is changing the example enough to not be hitting the bug I can't tell. But my guess is the while code block is equivalent to @bawolff's function and the $list[$k] is the reference that is still live when it calls f again causing it to overwrite - which is why I think my bug is essentially the same. This isn't the case for all f. In my case f is the result of a database query defined by $q, and f results in a MySQL select, returning the number of results remaining. I simplified it down to mimicking that returning objects in turn from a short array instead, but that doesn't fail.By the way... (a) it's definitely $list[$obj->field] that's provoking the problem. If I change it to $list[$obj->anotherfield], then 'anotherfield' is the one that gets overwritten. (b) it's wrong on return from f, i.e. if I print the list before the clone line on the sedcond iteration, then the first element of the list has already been overwritten by f. It's not the assignment in the loop that is doing the overwriting. (c) if I change list to be an object instead of array, and have $list{$obj->field} = clone $obj it still fails$list["{$obj->field}"] = clone $obj also makes it work.