0

In some third party code, that I am not allowed to change, something peculiar is happening.

They write a variable (an array containing objects) to the session (without serialising it) and then iterate using a foreach on the original variable (without using references). Whenever they change a value, the corresponding value in the session is also changed. I was able to create a smaller example that has the same behaviour:

$test = array((object)array("categories" => "test"));

$_SESSION['woot'] = $test;

print_r($_SESSION['woot']);

foreach ($test as $a) {

    if (!is_array($a->categories)) $a->categories = array(); 

}

print_r($_SESSION['woot']);

This is the result:

Array
(
    [0] => stdClass Object
        (
            [categories] => test
        )

)
Array
(
    [0] => stdClass Object
        (
            [categories] => Array
                (
                )

        )

)

I already noticed that, when I serialize and unserialize the object array, the problem does not occur.

Does anyone have an idea about what's happening here? Is it the code? Is it an incorrect server setting? I'd like to know a little more before I contact the devs of the code.

Additional information:

  • I am using PHP Version 5.3.14
  • Register globals is switched off

Regards, Joost.

0

2 Answers 2

3

A variable holding an object only holds a reference to that object. Assigning an object (an object reference) from one variable to another does not make a copy of the object, there's still only one object. If you modify that object, all variables holding a reference to that object will see the change, because there's only one object instance.

If you want to make a copy of an object, you need to explicitly clone it.

Sign up to request clarification or add additional context in comments.

Comments

1

In PHP objects are passed by reference and PHP uses copy-on-write.

$test = array((object)array("categories" => "test"));
// it is still the same array
$_SESSION['woot'] = $test;
foreach ($test as $a) {
    // You change something in an object, which is passed by reference
    if (!is_array($a->categories)) $a->categories = array();
}

Serialization and deserialization creates new objects

$test = array((object)array("categories" => "test"));
// new array with new objects
$test = unserialize(serialize($test));

4 Comments

I always thought a foreach only used references when you specificly said it should by adding an &. foreach($a => &$b) Learned something again.
@jberculo It is true for all variable types except for objects, where &$a is default behavior and is same as $a.
There is one thing, however. The third party claims that they can't reproduce this problem with the same codebase. Is there a PHP version component at work as well?
@jberculo This behavior is core concept of PHP. On the other hand as you said you provided small example, there may be other things in play.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.