php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #42316 ArrayIterator mix refcount and reference
Submitted: 2007-08-16 08:18 UTC Modified: 2007-08-17 06:14 UTC
From: titerm at lfdj dot com Assigned:
Status: Not a bug Package: SPL related
PHP Version: 5.2.4RC3-dev OS: Windows
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: titerm at lfdj dot com
New email:
PHP Version: OS:

 

 [2007-08-16 08:18 UTC] titerm at lfdj dot com
Description:
------------
When multiple entries of an array are initialized with a $base var array, new entries will increase refcount.
Any modification of this new entries should decrease refcount.

But if modification are done through a recursive Array Iterator, refcount works like it was reference, and all leaf are modified

Reproduce code:
---------------
// Reference var array
$base['one']['two'] = 'ORG';

// Work array
$arr = array (
	'l1' => $base,
	'l2' => $base,
	'l3' => $base,
);
unset($base); // No need anymore of reference 

// Break refcount for level 2, level 1 and level 3 are still refcount
$arr['l2']['one']['two'] = $arr['l2']['one']['two'];

// Create an iterator on level 1
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr['l1']));
// Alter leaf of level 1
$iterator->offSetSet($iterator->key(), 'ALTER' );
debug_zval_dump($arr);


Expected result:
----------------
array(3) refcount(2){
  ["l1"]=>
  array(1) refcount(1){
    ["one"]=>
    string(5) "ALTER" refcount(1)
  }
  ["l2"]=>
  array(1) refcount(1){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(3) "ORG" refcount(1)
    }
  }
  ["l3"]=>
  array(1) refcount(1){
    ["one"]=>
    string(3) "ORG" refcount(1)
  }
}

Actual result:
--------------
array(3) refcount(2){
  ["l1"]=>
  array(1) refcount(3){
    ["one"]=>
    string(5) "ALTER" refcount(1)
  }
  ["l2"]=>
  array(1) refcount(1){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(3) "ORG" refcount(1)
    }
  }
  ["l3"]=>
  array(1) refcount(3){
    ["one"]=>
    string(5) "ALTER" refcount(1)
  }
}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-08-16 08:32 UTC] titerm at lfdj dot com
I have simplified the reproduce code to much. Sorry

it should be

$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr['l1']));
foreach($iterator as $leaf) {
    $iterator->offSetSet($iterator->key(), 'ALTER' );
}

Expected result
array(3) refcount(2){
  ["l1"]=>
  array(1) refcount(1){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(5) "ALTER" refcount(1)
    }
  }
  ["l2"]=>
  array(1) refcount(1){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(3) "ORG" refcount(2)
    }
  }
  ["l3"]=>
  array(1) refcount(1){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(3) "ORG" refcount(1)
    }
  }
}

Actual Result 
array(3) refcount(2){
  ["l1"]=>
  array(1) refcount(3){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(5) "ALTER" refcount(1)
    }
  }
  ["l2"]=>
  array(1) refcount(1){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(3) "ORG" refcount(2)
    }
  }
  ["l3"]=>
  array(1) refcount(3){
    ["one"]=>
    array(1) refcount(1){
      ["two"]=>
      string(5) "ALTER" refcount(1)
    }
  }
}
 [2007-08-16 10:45 UTC] jani@php.net
What if you didn't pay attention to the internals like refcount but only for actual results? Can you come up with a short and simple example of what actually doesn't work?
 [2007-08-16 12:11 UTC] titerm at lfdj dot com
Create a ArrayIterator on branch1. Alter the leaf by using iterator. Leave on branch2 are affected too.

Simplified reproduce code.

$data['one'] = 'ORG';
$arr = array (
	'branch1' => $data,
	'branch2' => $data,
);
$iterator = new ArrayIterator($arr['branch1']);;
$iterator->offSetSet($iterator->key(), 'ALTER' );
var_dump($arr);

Expected result
array
  'branch1' => 
    array
      'one' => string 'ALTER' (length=5)
  'branch2' => 
    array
      'one' => string 'ORG' (length=3)


Actual result
array
  'branch1' => 
    array
      'one' => string 'ALTER' (length=5)
  'branch2' => 
    array
      'one' => string 'ALTER' (length=5)
 [2007-08-16 13:47 UTC] jani@php.net
Since the iterator does not modify original array, this is the expected result of your script (and what I get using latest CVS build):

array(2) {
  ["branch1"]=>
  array(1) {
    ["one"]=>
    string(3) "ORG"
  }
  ["branch2"]=>
  array(1) {
    ["one"]=>
    string(3) "ORG"
  }
}

 [2007-08-16 14:07 UTC] titerm at lfdj dot com
I did my test on 5.2.3 release on window and solaris.
I don't understand why you say original array is not modified since i'm using offsetSet function, it should modified $arr and it does.

If i init $arr with $data1 and $data2 like this, only branch one is altered.

$data1['one'] = 'ORG';
$data2['one'] = 'ORG';
$arr = array (
	'branch1' => $data1,
	'branch2' => $data2,
);
 [2007-08-17 00:58 UTC] jani@php.net
But I'm using latest CVS. Perhaps you should have tried it too before reporting already fixed bug.
 [2007-08-17 05:26 UTC] titerm at lfdj dot com
I can't access to cvs or svn through my company's firewall.
I see  there is a zip with snaps for windows but the download server is currently offline for maintenance.

Anyway, with the result you gave me, 'ORG' on both branch, the problem is not solve for me. ArrayIterator should be able to modify the array. And at least, it has always been able too until now.
 [2007-08-17 06:14 UTC] titerm at lfdj dot com
I finally download snap.
This snapshot was automatically generated on
Fri, 17 Aug 2007 00:08:51 -0400

Version: 5.2.4RC3-dev
Branch: HEAD
Build: Release_TS

The bug is still there. Take a look to the added dump of $data. $data is altered to. This can't be the expected result. $data is never used as reference.

Reproduce code
$data[] = 'ORG';
$arr1 = array ('branch1' => $data, 'branch2' => $data);

$iterator = new ArrayIterator($arr1['branch1']);;
$iterator->offSetSet($iterator->key(), 'ALTER' );

var_dump($arr1);
var_dump($data);

Actual result.
array(2) {
  ["branch1"]=>
  array(1) {
    [0]=>
    string(5) "ALTER"
  }
  ["branch2"]=>
  array(1) {
    [0]=>
    string(5) "ALTER"
  }
}
array(1) {
  [0]=>
  string(5) "ALTER"
}
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat May 11 00:01:31 2024 UTC