Permalink
Browse files

[Finder] Fix iteration fails with non-rewindable streams

  • Loading branch information...
1 parent 69858f8 commit ee540fa7b9db90ae9f0c78e4cb5398b8476a1326 @alquerci alquerci committed with fabpot May 24, 2013
@@ -30,7 +30,14 @@ public function rewind()
{
$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();
}
@@ -20,6 +20,11 @@
*/
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
+ /**
+ * @var Boolean
+ */
+ private $rewindable;
+
public function __construct($path, $flags)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
@@ -38,4 +43,39 @@ public function current()
{
return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
}
+
+ /**
+ * Do nothing for non rewindable stream
+ */
+ public function rewind()
+ {
+ if (false === $this->isRewindable()) {
+ return;
+ }
+
+ 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;
+ }
}
View
@@ -459,4 +459,21 @@ public function testMultipleLocations()
$this->assertEquals(1, count($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);
+ }
}
@@ -29,4 +29,41 @@ protected function assertOrderedIterator($expected, \Traversable $iterator)
$this->assertEquals($expected, array_values($values));
}
+
+ /**
+ * Same as IteratorTestCase::assertIterator with foreach usage
+ *
+ * @param array $expected
+ * @param \Traversable $iterator
+ */
+ protected function assertIteratorInForeach($expected, \Traversable $iterator)
+ {
+ $values = array();
+ foreach ($iterator as $file) {
+ $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
+ $values[] = $file->getPathname();
+ }
+
+ sort($values);
+ sort($expected);
+
+ $this->assertEquals($expected, array_values($values));
+ }
+
+ /**
+ * Same as IteratorTestCase::assertOrderedIterator with foreach usage
+ *
+ * @param array $expected
+ * @param \Traversable $iterator
+ */
+ protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator)
+ {
+ $values = array();
+ foreach ($iterator as $file) {
+ $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
+ $values[] = $file->getPathname();
+ }
+
+ $this->assertEquals($expected, array_values($values));
+ }
}
@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+*
+* (c) Fabien Potencier <fabien@symfony.com>
+*
+* 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;
+ }
+}

0 comments on commit ee540fa

Please sign in to comment.