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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
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: Thu May 23 03:01:28 2024 UTC