Bug #76863 [Com]: Creating array reference grants direct access to the property
| From: | spam2 at rhsoft dot net | Date: | Tue, 11 Sep 2018 19:21:04 +0000 |
| Subject: | Bug #76863 [Com]: Creating array reference grants direct access to the property | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-216990@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=76863&edit=1
ID: 76863
Comment by: spam2 at rhsoft dot net
Reported by: tshumbeo at mailhouse dot biz
Summary: Creating array reference grants direct access to the
property
Status: Open
Type: Bug
Package: Scripting Engine problem
Operating System: Linux and Windows
PHP Version: 7.2.9
Block user comment: N
Private report: N
New Comment:
> reating a reference *to* an array member (1) should *not*
> turn that member into a reference
that assumption is simply wrong
the golden rule in PHP is not to use references at all until you really know what you are doing, are
arwa about all side-effects and are relly sure given the copy-on-write nature of PHP you gain
anything
below a sample for side-effects of references which i show everyone who thinks references are a god
think - don't confuse PHP references with C pointers
----
BAD:
<?php
$array1 = [1, 2, 3];
$array2 = [5, 6, 7];
foreach($array1 as $key=>&$item)
{
}
foreach($array2 as $key=>$item)
{
$item = "MY GOD $key";
}
print_r($array1);
print_r($array2);
$item = 'GO AWAY';
print_r($array1);
?>
-------
CLEAN BUT USELESS:
<?php
$array1 = [1, 2, 3];
$array2 = [5, 6, 7];
foreach($array1 as $key=>&$item)
{
}
unset($item); /** FIX */
foreach($array2 as $key=>$item)
{
$item = "MY GOD $key";
}
print_r($array1);
print_r($array2);
$item = 'GO AWAY';
print_r($array1);
?>
Previous Comments:
------------------------------------------------------------------------
[2018-09-11 19:14:16] tshumbeo at mailhouse dot biz
Description:
------------
Tested on PHP 5.6.37 and 7.2.9, Windows and Linux, various configurations.
See the test script. When (1) is present (assignment by reference) and (2) is missing (no
unsetting), it appears that K->$a[0] itself becomes a reference (not $unusedVariable, which would
be expected). When K->a() - a typical read accessor method - returns the array, modifying the
array's member affects the K->$a.
If (2) is present, then the $a[0] reference is removed and it's no more possible to change the
K->$a by changing K->a() members.
(1) | (2) | Result
----|-------|-------
& | unset | no bug
| unset | no bug
| | no bug
& | | bug!
A practical consequence: I am unable to create a reference to the internal array's members (for
use in other places) without opening doors to direct modification of the property which can happen
inadvertently (this is how I stumbled upon it):
function clean(array $ar) {
foreach ($ar as &$ref) $ref = ...;
return $ar;
}
clean($k->a());
Do note that clean() doesn't accept $a by reference, it simply uses it as a copy for returning
in the result. However, due to the bug it in fact directly modifies $k's internal $a array!
Test script:
---------------
class K {
private $a = ['a'];
function a() {
return $this->a;
}
function f($f) {
// (1)
$unusedVariable = &$this->a[0];
// (2)
//unset($unusedVariable);
$f();
}
}
$k = new K;
$k->f(function () use ($k) {
foreach ($k->a() as &$ref) { $ref = 123; }
});
// Expected result: string 'a'.
// Actual result: int 123.
var_dump($k->a());
Expected result:
----------------
Creating a reference *to* an array member (1) should *not* turn that member into a reference (to
itself?).
Actual result:
--------------
Creating a reference turns that member into a sticky reference that isn't removed by array
cloning functions like array_values(), array_merge(), unset(), etc. and can be removed only by
unsetting (2) the referencing variable (even if it's out of scope the reference remains).
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=76863&edit=1