php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75158 Cloning objects whith members that hold references to other members
Submitted: 2017-09-05 15:27 UTC Modified: 2017-09-06 09:04 UTC
From: krulis at ksi dot mff dot cuni dot cz Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 7.1.9 OS: all
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: krulis at ksi dot mff dot cuni dot cz
New email:
PHP Version: OS:

 

 [2017-09-05 15:27 UTC] krulis at ksi dot mff dot cuni dot cz
Description:
------------
When I have an object and one member variable is a reference to another member variable, clone of such object is not correctly constructed. In the test script, you can see Foo class with two members: $data and $ref. $ref is set to be a reference to $data of the same object.

When object of foo is cloned. The clone's $data is in fact reference to original object's $data. In other words, clone should in fact do $c->data = $o->data (shallow copy by = operator), but instead, it seems like $c->data =& $o->data.

Note that the references are in fact not used, and when they are removed (line in the constructor is commented), it starts to work correctly again. I have also tested integer values in $data (beside arrays) and they work all the same.

This behavior was observed on Windows 7.1.9 and Linux 5.6.26. A similar problem was described in #66040, but I am not sure, whether they are connected or not.

Test script:
---------------
<?php
class Foo {
	public $data = [];
	public $ref;
	public function __construct()
	{
		$this->ref =& $this->data;
	}
}

$o = new Foo();
$c = clone($o);
$o->data[] = 42;

var_dump($c->data);  // writes out an array with one item (42), even though the array should be empty

Expected result:
----------------
I would expect that $c->data === [] at the end.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-09-05 17:15 UTC] danack@php.net
I don't think this is a bug......it's just that references are really, really, confusing.

For the line "$this->ref =& $this->data;" this does not create a variable called 'ref' that is a reference to the 'real' variable called 'data'. 

Instead it moves the variable elsewhere and sets both 'data' and 'ref' to be a reference to that variable. This in unlike other programming languages that use pointers. In those, creating a point to a value does not affect how the original value is stored. But in PHP, creating a reference does make the original property become a reference.

A workaround for this is to explicitly copy the values, destroy the variable with unset() to remove the fact that it's a reference to another variable, and then copy the values over.

class Foo {
    public $data;
    public $ref;
    public function __construct() {
        $this->data = [];
        $this->ref = &$this->data;
    }

    public function __clone() {
        $values = (array)$this->data;
        unset($this->data);
        $this->data = [$values];
        $this->ref = &$this->data;
    }
}

$o = new Foo();
$o->data[] = 1;

$c = clone($o);
$o->data[] = 42;
var_dump($c->data);


Or you could just not use references.
 [2017-09-06 09:04 UTC] danack@php.net
-Status: Open +Status: Not a bug
 [2017-09-06 09:04 UTC] danack@php.net
Closing as not a bug, because I think that's how references 'work'.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Sat Oct 24 04:01:23 2020 UTC