Bug #75632 [Com]: Wrong behavior of clone and references
| From: | kripper at imatronix dot com | Date: | Sat, 10 Feb 2018 20:09:32 +0000 |
| Subject: | Bug #75632 [Com]: Wrong behavior of clone and references | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-213908@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
Comment by: kripper at imatronix dot com
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:
Thanks for your time.
I'm afraid that the posted code is not a solution for the general case.
My previous comments were not quite clear so I wrote something more elaborated here:
https://docs.google.com/document/d/11_uIZyvXzPBTv8-1id-xl9itYpJ4lGaSquWhEvmPgK4/edit#heading=h.lbjyy98sok62
Previous Comments:
------------------------------------------------------------------------
[2017-12-06 08:35:24] requinix@php.net
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.
------------------------------------------------------------------------
[2017-12-05 21:39:41] kripper at imatronix dot com
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.
------------------------------------------------------------------------
[2017-12-05 19:13:07] requinix@php.net
> // 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
------------------------------------------------------------------------
[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
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
https://bugs.php.net/bug.php?id=75632
--
Edit this bug report at https://bugs.php.net/bug.php?id=75632&edit=1