php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #62914 First entry appears twice, unless rewind method is called first
Submitted: 2012-08-23 22:18 UTC Modified: 2014-03-28 19:44 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: lveyde at gmail dot com Assigned: levim (profile)
Status: Closed Package: SPL related
PHP Version: 5.3.16 OS: CentOS 6.3
Private report: No CVE-ID: None
 [2012-08-23 22:18 UTC] lveyde at gmail dot com
Description:
------------
---
From manual page: http://www.php.net/recursivedirectoryiterator.construct
---

I tried to run the sample code (changing the directory), and unless the call to the rewind() method is performed before using the iterator, the first entry appears twice in the output.

I'm using PHP version 5.3.3, the latest available with RHEL/CentOS 6.x.

<?php

$directory = '/root/isos';

$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));

// Added this
$it->rewind();

while($it->valid()) {

    if (!$it->isDot()) {
        echo 'SubPathName: ' . $it->getSubPathName() . "\n";
        echo 'SubPath:     ' . $it->getSubPath() . "\n";
        echo 'Key:         ' . $it->key() . "\n\n";
    }

    $it->next();
}

?>

Is that a normal behavior (and thus bug in the documentation) ?

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

$directory = '/root/isos';

$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));

while($it->valid()) {

    if (!$it->isDot()) {
        echo 'SubPathName: ' . $it->getSubPathName() . "\n";
        echo 'SubPath:     ' . $it->getSubPath() . "\n";
        echo 'Key:         ' . $it->key() . "\n\n";
    }

    $it->next();
}

?>

Expected result:
----------------
SubPathName: CentOS-6.2-x86_64-netinstall.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-netinstall.iso

SubPathName: CentOS-6.2-x86_64-bin-DVD1.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-bin-DVD1.iso

SubPathName: CentOS-6.2-x86_64-LiveCD.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-LiveCD.iso

SubPathName: CentOS-6.2-x86_64-bin-DVD2.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-bin-DVD2.iso


Actual result:
--------------
SubPathName: CentOS-6.2-x86_64-netinstall.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-netinstall.iso

SubPathName: CentOS-6.2-x86_64-netinstall.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-netinstall.iso

SubPathName: CentOS-6.2-x86_64-bin-DVD1.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-bin-DVD1.iso

SubPathName: CentOS-6.2-x86_64-LiveCD.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-LiveCD.iso

SubPathName: CentOS-6.2-x86_64-bin-DVD2.iso
SubPath:
Key:         /root/isos/CentOS-6.2-x86_64-bin-DVD2.iso


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-08-26 14:52 UTC] phpbugs at jessemccarthy dot net
I'm running into this too, in PHP 5.3.6 (Apache module / CLI) on Ubuntu 11.10.

Test case directory structure:

test/
        a/
                b.php
        iterate.php
        while.php
        foreach.php
        rewind-while.php
        iterator_to_array.php
        

iterate.php is used in all of the other scripts:    
    
<?php

header( "Content-Type: text/plain" );

$it = new RecursiveIteratorIterator(

  new RecursiveDirectoryIterator(

    __DIR__ . "/a",

    FilesystemIterator::SKIP_DOTS

  ),

  RecursiveIteratorIterator::CHILD_FIRST

);


function show_items( $items ) {

  var_dump( $items );

}


$items = array();


****************************************
Expected result from all scripts:
****************************************

array(1) {
  [0]=>
  string(5) "b.php"
}


Content of the test scripts and actual results:

****************************************
while.php
****************************************

<?php

require_once "iterate.php";

while ( $it->valid() ) {

  $items[] = $it->getFilename();

  $it->next();

}

show_items( $items );

========================================

array(2) {
  [0]=>
  string(5) "b.php"
  [1]=>
  string(5) "b.php"
}


****************************************
foreach.php
****************************************

<?php

require_once "iterate.php";

foreach ( $it as $file ) {

  $items[] = $file->getFilename();

}

show_items( $items );

========================================

array(1) {
  [0]=>
  string(5) "b.php"
}


****************************************
rewind-while.php
****************************************

<?php

require_once "iterate.php";

$it->rewind();

require_once "while.php";

========================================

array(1) {
  [0]=>
  string(5) "b.php"
}


****************************************
iterator_to_array.php
****************************************

<?php

require_once "iterate.php";

$items = array_map( function ( $item ) {

  return $item->getFilename();

}, array_values( iterator_to_array( $it ) ) );

show_items( $items );

========================================

array(1) {
  [0]=>
  string(5) "b.php"
}


As you can see, iterating with `while` and `next()` produces an unexpected 
duplicate value, whereas `foreach`, `iterator_to_array()`, and the `rewind()` 
then `while` suggested by the OP produce the expected result.
 [2012-09-13 05:14 UTC] googleguy@php.net
You stated you're using PHP version 5.3.3 in your bug description, but your bug 
report says 5.3.16. I can not reproduce this behavior in PHP 5.3.16, however I have 
found that it is evident in early version of 5.3, prior to 5.3.10.

Steps to reproduce:

1) Create a temporary directory somewhere and your file system and create some 
empty files in that directory for testing.

`cd /tmp`
`mkdir test`
`touch test1 & touch test2 & touch test3`

2) Run the following PHP script

<?php
$directory = "/tmp/test"; 
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
while ($it->valid()) { if (!$it->isDot()) var_dump($it->key()); $it->next(); }
?>

Here's the output I got in PHP 5.3.2 (order is not important here):

string(15) "/tmp/test/test3"
string(15) "/tmp/test/test3"
string(15) "/tmp/test/test1"
string(15) "/tmp/test/test2"

---------------------------------

Here's the output I got in 5.3.10 all the way to the latest 5.4

string(15) "/tmp/test/test2"
string(15) "/tmp/test/test3"
string(15) "/tmp/test/test1"

------------------------------

The only potential documentation bug here is that the changelog doesn't reflect any 
changes in the behavior of RecursiveDirectoryIterator that would indicate why this 
bug existed. I haven't been able to find any bug reports on the issue, but 
obviously it was a problem at some point. I'll see if we need to update the 
changelog about this behavior and if necessary will open a bug report for SPL.
 [2013-01-19 15:15 UTC] googleguy@php.net
-Assigned To: +Assigned To: salathe
 [2013-11-20 11:44 UTC] duke at ejoom dot com
Small script to reproduce bug:

<?php
    $arr = array('first', 'second' => array('subsecond'));
    $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($arr));
    var_dump($it->current());
    $it->next();
    var_dump($it->current());

    $it->rewind();
    var_dump($it->current());
    $it->next();
    var_dump($it->current());
?>

results:

string 'first' (length=5)
string 'first' (length=5)
string 'first' (length=5)
string 'subsecond' (length=9)

expects:

string 'first' (length=5)
string 'subsecond' (length=9)
string 'first' (length=5)
string 'subsecond' (length=9)


PHP 5.5.5, Ubuntu.
 [2014-03-28 19:39 UTC] levim@php.net
-Assigned To: salathe +Assigned To: levim
 [2014-03-28 19:39 UTC] levim@php.net
Iterators must be rewound before being used. The documentation is in error here, even if something works without being rewound in certain PHP versions. I'll update the documentation, then mark this as closed.
 [2014-03-28 19:44 UTC] levim@php.net
-Status: Assigned +Status: Closed
 [2014-03-28 19:44 UTC] levim@php.net
The example has been updated to reflect the call to rewind. As a reminder, all iterators have been designed in a way that you need to call rewind before using.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Nov 22 11:01:29 2024 UTC