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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Sun Dec 22 11:01:30 2024 UTC