Search code examples
phpclonearrayiterator

How do i clone an ArrayIterator in PHP?


I am trying to clone an \ArrayIterator object, but it seems like the cloned one is still referencing to the original one.

$list = new \ArrayIterator;
$list->append('a');
$list->append('b');

$list2 = clone $list;
$list2->append('c');
$list2->append('d');

// below result prints '4', i am expecting result '2'
echo $list->count();

Anyone have explanation to this behavior? Thank you in advance.


Solution

  • Though I am having difficulty locating documentation that explicitly says so, internally ArrayIterator's private $storage property wherein the array is held must be a reference to an array rather than the array itself directly stored within the object.

    The documentation on clone says that

    PHP 5 will perform a shallow copy of all of the object's properties. Any properties that are references to other variables will remain references.

    So when you clone the ArrayIterator object, the newly cloned object contains a reference to the same array as the original one. Here is an old bug report wherein this behavior is said to be the expected behavior.

    If you want to copy the current state of an ArrayIterator, you might consider instead instantiating a new one using the array returned by getArrayCopy()

    $iter = new \ArrayIterator([1,2,3,4,5]);
    
    // Copy out the array to instantiate a new one
    $copy = new \ArrayIterator($iter->getArrayCopy());
    // Modify it
    $copy->append(6);
    
    var_dump($iter); // unmodified
    php > var_dump($iter);
    class ArrayIterator#1 (1) {
      private $storage =>
      array(5) {
        [0] =>
        int(1)
        [1] =>
        int(2)
        [2] =>
        int(3)
        [3] =>
        int(4)
        [4] =>
        int(5)
      }
    }
    
    var_dump($copy); // modified
    class ArrayIterator#2 (1) {
      private $storage =>
      array(6) {
        [0] =>
        int(1)
        [1] =>
        int(2)
        [2] =>
        int(3)
        [3] =>
        int(4)
        [4] =>
        int(5)
        [5] =>
        int(6)
      }
    }
    

    The above is a simple operation though, and only creates a new ArrayIterator with the currently stored array as the original. It does not maintain the current iteration state. To do that, you would need to also call seek() to advance the pointer to the desired position. Here is a thorough answer explaining how that could be done.