php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #63823 SPL InfiniteIterator needs explicit rewind to be usable in a closure
Submitted: 2012-12-21 02:17 UTC Modified: 2013-02-05 17:34 UTC
From: sixd@php.net Assigned:
Status: Wont fix Package: SPL related
PHP Version: 5.4Git-2012-12-21 (Git) OS: Linux
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: sixd@php.net
New email:
PHP Version: OS:

 

 [2012-12-21 02:17 UTC] sixd@php.net
Description:
------------
SPL's InfiniteIterator returns NULL for the first access, unless used in a 
'foreach' loop, or a rewind is done.

This means that an explicit rewind is needed before an InfiniteIterator can be 
safely be used in an application.

Test script:
---------------
<?php

$b = array('one', 'two', 'three');
$b_it = new InfiniteIterator(new ArrayIterator($b));
for ($i = 0; $i < 7; $i++) {
    var_dump($b_it->current()); 
    $b_it->next();
}

?>

Expected result:
----------------
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "one"

Actual result:
--------------
NULL
string(3) "two"
string(5) "three"
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "one"

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-12-21 02:20 UTC] sixd@php.net
A bigger example is:
<?php

echo "'foreach' on ArrayIterator\n";
$c = array('one', 'two', 'three');
$c_it = new ArrayIterator($c);
foreach ($c_it as $key => $val) {
    var_dump($val);
}

echo "\n'foreach' on InfiniteIterator\n";

$a = array('one', 'two', 'three');
$a_it = new InfiniteIterator(new ArrayIterator($a));
$i = 0;
foreach ($a_it as $key => $val) {
    if ($i++ >= 7) break;
    var_dump($val);
}

echo "\n'for' on ArrayIterator\n";
$d = array('one', 'two', 'three');
$d_it = new ArrayIterator($d);
for ($i = 0; $i < 3; $i++) {
    var_dump($d_it->current()); 
    $d_it->next();
}

echo "\n'for' on InfiniteIterator\n";

$b = array('one', 'two', 'three');
$b_it = new InfiniteIterator(new ArrayIterator($b));
for ($i = 0; $i < 7; $i++) {
    var_dump($b_it->current()); 
    $b_it->next();
}

?>

This outputs:
'foreach' on ArrayIterator
string(3) "one"
string(3) "two"
string(5) "three"

'foreach' on InfiniteIterator
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "one"

'for' on ArrayIterator
string(3) "one"
string(3) "two"
string(5) "three"

'for' on InfiniteIterator
NULL
string(3) "two"
string(5) "three"
string(3) "one"
string(3) "two"
string(5) "three"
string(3) "one"

So the odd behavior is with the last loop.

A "real" life script (from discussion on php.internals) is:

<?php

// Replacing words in a string with a sequential, repeating set of replacement words

$replacements = array('one', 'two', 'three');

$replacements_iterator = new InfiniteIterator(new ArrayIterator($replacements));
$replacements_iterator->rewind();  // why is the rewind needed?

$result = preg_replace_callback(
    '/word/',
    function($matches) use ($replacements_iterator) {
        $r = $replacements_iterator->current();
        $replacements_iterator->next();
        return $r;
    },
    'word word word word word'
); 

var_dump($result);

// Outputs: 
//    string(21) "one two three one two"
// Without the call to $replacements_iterator->rewind(), the output is:
//    string(18) " two three one two"

?>
 [2012-12-21 02:35 UTC] laruence@php.net
I saw similar report before, and remembered someone mark it as won't fix.. I just 
can not find the previous report now...
 [2012-12-23 18:02 UTC] felipe@php.net
Are you referring to bug #44063?
 [2013-02-05 17:34 UTC] levim@php.net
-Status: Open +Status: Wont fix
 [2013-02-05 17:34 UTC] levim@php.net
By design, iterators are required to call rewind before using them.  The foreach 
code does this for you, but since you aren't using a foreach construct and you 
didn't call rewind yourself it isn't happening. You are effectively using 
undefined behavior whenever you do this.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 09:01:30 2024 UTC