Zend Framework

Zend_config : toArray conflict with iterator implements.

Details

  • Type: Bug Bug
  • Status: Resolved Resolved
  • Priority: Trivial Trivial
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: 1.8.4
  • Component/s: Zend_Config
  • Labels:
    None

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.

Activity

Hide
Rob Allen added a comment -

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

Thanks,

Rob...

Show
Rob Allen added a comment - Would you mind providing the code you're using along with the results you're getting and the results you are expecting? Thanks, Rob...
Hide
julien PAULI added a comment -

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

Could you provide a use case ?

Show
julien PAULI added a comment - Foreach() uses the array directly and thus, the internal array iterator and not Zend_Config's iterator. Could you provide a use case ?
Hide
Tomoaki Kosugi added a comment -

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() .

Show
Tomoaki Kosugi added a comment - 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() .
Hide
CHOTIN Nicolas added a comment -

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.

Show
CHOTIN Nicolas added a comment - 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.
Hide
julien PAULI added a comment -

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

Show
julien PAULI added a comment - 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
Hide
Rob Allen added a comment -

julien,

Fancy writing a unit test and then patching?

Regards,

Rob...

Show
Rob Allen added a comment - julien, Fancy writing a unit test and then patching? Regards, Rob...
Hide
julien PAULI added a comment -

I'm on it

Show
julien PAULI added a comment - I'm on it
Hide
julien PAULI added a comment -

Fixed in SVN r16090

Show
julien PAULI added a comment - Fixed in SVN r16090

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: