diff --git a/src/Symfony/Component/Finder/Iterator/FilterIterator.php b/src/Symfony/Component/Finder/Iterator/FilterIterator.php index 64da508ebd..f4da44c4cd 100644 --- a/src/Symfony/Component/Finder/Iterator/FilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/FilterIterator.php @@ -30,7 +30,14 @@ abstract class FilterIterator extends \FilterIterator { $iterator = $this; while ($iterator instanceof \OuterIterator) { - if ($iterator->getInnerIterator() instanceof \FilesystemIterator) { + $innerIterator = $iterator->getInnerIterator(); + + if ($innerIterator instanceof RecursiveDirectoryIterator) { + if ($innerIterator->isRewindable()) { + $innerIterator->next(); + $innerIterator->rewind(); + } + } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) { $iterator->getInnerIterator()->next(); $iterator->getInnerIterator()->rewind(); } diff --git a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php index 5e54d6fedd..91ec341869 100644 --- a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php +++ b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php @@ -20,6 +20,11 @@ use Symfony\Component\Finder\SplFileInfo; */ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator { + /** + * @var Boolean + */ + private $rewindable; + public function __construct($path, $flags) { if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { @@ -39,11 +44,41 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname()); } + /** + * Do nothing for non rewindable stream + */ public function rewind() { + if (false === $this->isRewindable()) { + return; + } + // @see https://bugs.php.net/bug.php?id=49104 parent::next(); parent::rewind(); } + + /** + * Checks if the stream is rewindable. + * + * @return Boolean true when the stream is rewindable, false otherwise + */ + public function isRewindable() + { + if (null !== $this->rewindable) { + return $this->rewindable; + } + + if (false !== $stream = @opendir($this->getPath())) { + $infos = stream_get_meta_data($stream); + closedir($stream); + + if ($infos['seekable']) { + return $this->rewindable = true; + } + } + + return $this->rewindable = false; + } } diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 4f419698e9..0a312ce37f 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -485,4 +485,21 @@ class FinderTest extends Iterator\RealIteratorTestCase $this->assertIterator($expected, $finder); $this->assertIteratorInForeach($expected, $finder); } + + public function testNonSeekableStream() + { + try { + $i = Finder::create()->in('ftp://ftp.mozilla.org/')->depth(0)->getIterator(); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', 'ftp')); + } + + $contains = array( + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub', + ); + + $this->assertIteratorInForeach($contains, $i); + } } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php new file mode 100644 index 0000000000..f762514346 --- /dev/null +++ b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php @@ -0,0 +1,83 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Symfony\Component\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; + +class RecursiveDirectoryIteratorTest extends IteratorTestCase +{ + /** + * @dataProvider getPaths + * + * @param string $path + * @param Boolean $seekable + * @param Boolean $supports + * @param string $message + */ + public function testRewind($path, $seekable, $contains, $message = null) + { + try { + $i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', $path)); + } + + $i->rewind(); + + $this->assertTrue(true, $message); + } + + /** + * @dataProvider getPaths + * + * @param string $path + * @param Boolean $seekable + * @param Boolean $supports + * @param string $message + */ + public function testSeek($path, $seekable, $contains, $message = null) + { + try { + $i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', $path)); + } + + $actual = array(); + + $i->seek(0); + $actual[] = $i->getPathname(); + + $i->seek(1); + $actual[] = $i->getPathname(); + + $i->seek(2); + $actual[] = $i->getPathname(); + + $this->assertEquals($contains, $actual); + } + + public function getPaths() + { + $data = array(); + + // ftp + $contains = array( + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub', + ); + $data[] = array('ftp://ftp.mozilla.org/', false, $contains); + + return $data; + } +}