views:

97

answers:

3
+2  Q: 

iterator_to_array

DatePeriod is a PHP class for handling recurring dates. It has a very limited number of methods. So when I want to do basic array functions with the recurring dates, I have to copy it to an array with iterator_to_array. Strangely, copying it seems to clobber it. Any ideas why?

$p=new DatePeriod(date_create('2008-01-01'),
                  DateInterval::createFromDateString( "+2 days" ),
                  date_create('2008-12-31')); 

echo count(iterator_to_array($p)); //183 

$a=iterator_to_array($p); 
echo count($a); //0
+1  A: 

I wonder if maybe the iterator isn't being re-wound by iterator_to_array(), so the second call starts iterating with the cursor at the end. You could try this:

$p->rewind()
$a=iterator_to_array($p);

If the iterator is not rewindable, you could try cloning the object before you traverse it, e.g.

$p2 = clone $p;
echo count(iterator_to_array($p2));

$array = iterator_to_array($p);
Tom Haigh
It appears that DatePeriod does not have a rewind method. :(The documentation says that DatePeriod implements Traversable. But Traversable is an abstract interface that requires Iterator or IteratorAggregate to be implemented (neither of which are mentioned in the DatePeriod docs). IteratorAggregate does not appear to have a public rewind method, so perhaps DatePeriod is a child of it rather than Iterator?
dnagirl
Sadly, clone is not an option. It appears to cause a fatal error, but of what type I don't know since it doesn't show up in the web logs.
dnagirl
As far as I'm concerned the new date and time implementations in PHP 5.3 suck big time.
Ionuț G. Stan
+1  A: 

Presumably, the first call traverses all the elements in the iterator (i.e. calls next() until valid() is false). The sensible behaviour is for iterator_to_array to begin the conversion from the current position in the iterator - having it silently rewind would be inflexible, and possibly bug inducing.

Try rewinding the iterator before using it again.

$p=new DatePeriod(date_create('2008-01-01'),
                  DateInterval::createFromDateString( "+2 days" ),
                  date_create('2008-12-31')); 

echo count(iterator_to_array($p)); //183 

$p->rewind(); // Newly added!

$a=iterator_to_array($p); 
echo count($a); //0
Adam Wright
+1  A: 

Here's what I'd do. I'd extend DatePeriod and implement a toArray method:

class MyDatePeriod extends DatePeriod
{
    public $dates;

    public function toArray()
    {
        if ($this->dates === null) {
            $this->dates = iterator_to_array($this);
        }

        return $this->dates;
    }
}

$p = new MyDatePeriod(date_create('2008-01-01'),
                      DateInterval::createFromDateString( "+2 days" ),
                      date_create('2008-12-31'));

echo count($p->toArray()) . "\n"; // 183

echo count($p->toArray()) . "\n"; // 183
Ionuț G. Stan
I don't understand why iterator_to_array($this) from inside the class doesn't do the same as iterator_to_array($p) from outside. But I'm sure glad it does! Thanks very much.
dnagirl
It does the same, but I'm caching the result into an instance variable, `$dates`, so on subsequent calls to `toArray` there are no more calls to `iterator_to_array`. I've simply returned the cached result.
Ionuț G. Stan