10000 [Finder] Fix iteration fails with non-rewindable streams · symfony/symfony@169c0b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 169c0b9

Browse files
alquercifabpot
authored andcommitted
[Finder] Fix iteration fails with non-rewindable streams
1 parent e789a02 commit 169c0b9

File tree

5 files changed

+185
-1
lines changed

5 files changed

+185
-1
lines changed

src/Symfony/Component/Finder/Iterator/FilterIterator.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ public function rewind()
3030
{
3131
$iterator = $this;
3232
while ($iterator instanceof \OuterIterator) {
33-
if ($iterator->getInnerIterator() instanceof \FilesystemIterator) {
33+
$innerIterator = $iterator->getInnerIterator();
34+
35+
if ($innerIterator instanceof RecursiveDirectoryIterator) {
36+
if ($innerIterator->isRewindable()) {
37+
$innerIterator->next();
38+
$innerIterator->rewind();
39+
}
40+
} elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) {
3441
$iterator->getInnerIterator()->next();
3542
$iterator->getInnerIterator()->rewind();
3643
}

src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
*/
2121
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
2222
{
23+
/**
24+
* @var Boolean
25+
*/
26+
private $rewindable;
27+
2328
public function __construct($path, $flags)
2429
{
2530
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
@@ -38,4 +43,39 @@ public function current()
3843
{
3944
return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
4045
}
46+
47+
/**
48+
* Do nothing for non rewindable stream
49+
*/
50+
public function rewind()
51+
{
52+
if (false === $this->isRewindable()) {
53+
return;
54+
}
55+
56+
parent::rewind();
57+
}
58+
59+
/**
60+
* Checks if the stream is rewindable.
61+
*
62+
* @return Boolean true when the stream is rewindable, false otherwise
63+
*/
64+
public function isRewindable()
65+
{
66+
if (null !== $this->rewindable) {
67+
return $this->rewindable;
68+
}
69+
70+
if (false !== $stream = @opendir($this->getPath())) {
71+
$infos = stream_get_meta_data($stream);
72+
closedir($stream);
73+
74+
if ($infos['seekable']) {
75+
return $this->rewindable = true;
76+
}
77+
}
78+
79+
return $this->rewindable = false;
80+
}
4181
}

src/Symfony/Component/Finder/Tests/FinderTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,4 +459,21 @@ public function testMultipleLocations()
459459

460460
$this->assertEquals(1, count($finder));
461461
}
462+
463+
public function testNonSeekableStream()
464+
{
465+
try {
466+
$i = Finder::create()->in('ftp://ftp.mozilla.org/')->depth(0)->getIterator();
467+
} catch (\UnexpectedValueException $e) {
468+
$this->markTestSkipped(sprintf('Unsupported stream "%s".', 'ftp'));
469+
}
470+
471+
$contains = array(
472+
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README',
473+
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html',
474+
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub',
475+
);
476+
477+
$this->assertIteratorInForeach($contains, $i);
478+
}
462479
}

src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,41 @@ protected function assertOrderedIterator($expected, \Traversable $iterator)
2929

3030
$this->assertEquals($expected, array_values($values));
3131
}
32+
33+
/**
34+
* Same as IteratorTestCase::assertIterator with foreach usage
35+
*
36+
* @param array $expected
37+
* @param \Traversable $iterator
38+
*/
39+
protected function assertIteratorInForeach($expected, \Traversable $iterator)
40+
{
41+
$values = array();
42+
foreach ($iterator as $file) {
43+
$this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
44+
$values[] = $file->getPathname();
45+
}
46+
47+
sort($values);
48+
sort($expected);
49+
50+
$this->assertEquals($expected, array_values($values));
51+
}
52+
53+
/**
54+
* Same as IteratorTestCase::assertOrderedIterator with foreach usage
55+
*
56+
* @param array $expected
57+
* @param \Traversable $iterator
58+
*/
59+
protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator)
60+
{
61+
$values = array();
62+
foreach ($iterator as $file) {
63+
$this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
64+
$values[] = $file->getPathname();
65+
}
66+
67+
$this->assertEquals($expected, array_values($values));
68+
}
3269
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Finder\Tests\Iterator;
13+
14+
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
15+
16+
class RecursiveDirectoryIteratorTest extends IteratorTestCase
17+
{
18+
/**
19+
* @dataProvider getPaths
20+
*
21+
* @param string $path
22+
* @param Boolean $seekable
23+
* @param Boolean $supports
24+
* @param string $message
25+
*/
26+
public function testRewind($path, $seekable, $contains, $message = null)
27+
{
28+
try {
29+
$i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
30+
} catch (\UnexpectedValueException $e) {
31+
$this->markTestSkipped(sprintf('Unsupported stream "%s".', $path));
32+
}
33+
34+
$i->rewind();
35+
36+
$this->assertTrue(true, $message);
37+
}
38+
39+
/**
40+
* @dataProvider getPaths
41+
*
42+
* @param string $path
43+
* @param Boolean $seekable
44+
* @param Boolean $supports
45+
* @param string $message
46+
*/
47+
public function testSeek($path, $seekable, $contains, $message = null)
48+
{
49+
try {
50+
$i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
51+
} catch (\UnexpectedValueException $e) {
52+
$this->markTestSkipped(sprintf('Unsupported stream "%s".', $path));
53+
}
54+
55+
$actual = array();
56+
57+
$i->seek(0);
58+
$actual[] = $i->getPathname();
59+
60+
$i->seek(1);
61+
$actual[] = $i->getPathname();
62+
63+
$i->seek(2);
64+
$actual[] = $i->getPathname();
65+
66+
$this->assertEquals($contains, $actual);
67+
}
68+
69+
public function getPaths()
70+
{
71+
$data = array();
72+
73+
// ftp
74+
$contains = array(
75+
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README',
76+
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html',
77+
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub',
78+
);
79+
$data[] = array('ftp://ftp.mozilla.org/', false, $contains);
80+
81+
return $data;
82+
}
83+
}

0 commit comments

Comments
 (0)
0