ZF-6995: Zend_config : toArray conflict with iterator implements.

Description

Function "toArray()" is created with a foreach on "_data". Interface "iterator" is implemented with : "next($this->_data)" and others functions of iterator directly on "_data".

My problem is function "toArray()" which is in conflict with the iterator interface. For fix that , we must create a clone of object and after we can use "toArray()".

But function "toArray()" should be NOT in conflict with iterator ?

For me, the problem is not "toArray()" but the implement of "Iterator". And is just when we want used "toArray()" AND iterator in same time on same object.

This problem could be apply to all classes with iterator and "toArray()". But I don't know if it's a usage problem or a bug.

Nicolas CHOTIN.

Comments

Would you mind providing the code you're using along with the results you're getting and the results you are expecting?

Thanks,

Rob...

Foreach() uses the array directly and thus, the internal array iterator and not Zend_Config's iterator.

Could you provide a use case ?

Hi I was put in a similar situation. toArray breaks iteration. At between before and after toArray, a behavior of valid and current is amusing.

Here is repro-code


<?php
$config = new Zend_Config(array(1,2,3));

$config->rewind();
var_dump($config->valid());  // true
var_dump($config->current()); // int(1)

$config->rewind();
$dummy = $config->toArray();
var_dump($config->valid()); // true
var_dump($config->current()); // false

2nd sample should be " not valid " or "valid? int(1)"

One Solution is clone $this->_data before "foreach($this->_data" In Zend_Config::toArray() .

In my case it's a controller for testing. I have create a config file with a "value" for config and a "value" for testing :

test.c1.regexp = ^/index/test test.c1.test = /index/test test.c2.regexp = ^/index/test1 test.c2.test = /index/test1

The initialization of object is in loop of tests, because this object is initialized only when used. But in real context we don't have a loop on same object "Zend_Config".

So for me it's a usage problem but it's interesting to add a warning in documentation mostly if Zend_Config it's not alone class in this case.

Ok I see.

toArray() works directly on the _data array, and so does the iterator. When calling toArray(), a foreach loop is used to iterate throught _data, that foreach loop first calls rewind() internaly , and then iterates. The problem is that the foreach loop leaves the array iterator at an undefinied position (that's why rewind() is called by foreach before the iteration). Then when you call current() on your object, it just says it doesn't have a current value.


$config = new Zend_Config(array(1,2,3));
$config->rewind(); // rewound
$config->toArray(); // the internal iterator is somewhere after the last position due to foreach loop
assert($config->current() == false); // OK

To see that, create a getIndex() method in Zend_Config, which return $_index; and try this :


$config = new Zend_Config(array(1,2,3));
$config->rewind(); // rewound
$config->toArray(); // the internal iterator is somewhere after the last position due to foreach loop
assert($config->key() == $config->getIndex()); // WRONG , $config->key() returns NULL while getIndex() has the right value

The patch is easy, trigger the internal PHP copy-on-write system to make foreach loop over a copy of the _data array, and not the real _data. Just add :


public function toArray()
    {
        $array = array();
+      $data = $this->_data;
        foreach ($this->_data as $key => $value) {
            if ($value instanceof Zend_Config) {
                $array[$key] = $value->toArray();
            } else {
                $array[$key] = $value;
            }
        }
        return $array;
    }

Even not using the new $data variable created, it increments the internal refcount on the _data structure, making foreach use a copy of it ;-)

julien,

Fancy writing a unit test and then patching?

Regards,

Rob...

I'm on it

Fixed in SVN r16090