Skip to content

Commit 99fa865

Browse files
bug #50884 [Finder] Fix initial directory is opened twice (mvorisek)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [Finder] Fix initial directory is opened twice | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #50851 | License | MIT | Doc PR | no The issue was introduced in #8120 which was coded wrongly for several reasons: - `seekable` support is about file/stream content seeking, not about rewind support, non-seekable stream handler can support rewind - it worked by coincidence - the original code involved additional directory open (syscall), which is/was slow - 1st rewind of DirectoryIterator is implicit, ie. it is not needed - again, skipping the rewind worked by coincidence - 2nd+ rewind must fail if it is not supported, the original code silently skipped it The test from #8120 is kept and is passing. I also improved the ftp support detection to make the test stricter. To support rewind for ftp I opened - php/php-src#11597 - but this Symfony PR does not need it. Commits ------- d3f03be [Finder] Fix initial directory is opened twice
2 parents 9f300e9 + d3f03be commit 99fa865

File tree

2 files changed

+22
-34
lines changed

2 files changed

+22
-34
lines changed

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

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
2929
/**
3030
* @var bool
3131
*/
32-
private $rewindable;
32+
private $ignoreFirstRewind = true;
3333

3434
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
3535
private $rootPath;
@@ -118,7 +118,6 @@ public function getChildren()
118118
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
119119

120120
// performance optimization to avoid redoing the same work in all children
121-
$children->rewindable = &$this->rewindable;
122121
$children->rootPath = $this->rootPath;
123122
}
124123

@@ -129,40 +128,30 @@ public function getChildren()
129128
}
130129

131130
/**
132-
* Do nothing for non rewindable stream.
133-
*
134131
* @return void
135132
*/
136133
#[\ReturnTypeWillChange]
137-
public function rewind()
134+
public function next()
138135
{
139-
if (false === $this->isRewindable()) {
140-
return;
141-
}
136+
$this->ignoreFirstRewind = false;
142137

143-
parent::rewind();
138+
parent::next();
144139
}
145140

146141
/**
147-
* Checks if the stream is rewindable.
148-
*
149-
* @return bool
142+
* @return void
150143
*/
151-
public function isRewindable()
144+
#[\ReturnTypeWillChange]
145+
public function rewind()
152146
{
153-
if (null !== $this->rewindable) {
154-
return $this->rewindable;
155-
}
156-
157-
if (false !== $stream = @opendir($this->getPath())) {
158-
$infos = stream_get_meta_data($stream);
159-
closedir($stream);
147+
// some streams like FTP are not rewindable, ignore the first rewind after creation,
148+
// as newly created DirectoryIterator does not need to be rewound
149+
if ($this->ignoreFirstRewind) {
150+
$this->ignoreFirstRewind = false;
160151

161-
if ($infos['seekable']) {
162-
return $this->rewindable = true;
163-
}
152+
return;
164153
}
165154

166-
return $this->rewindable = false;
155+
parent::rewind();
167156
}
168157
}

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@
1515

1616
class RecursiveDirectoryIteratorTest extends IteratorTestCase
1717
{
18+
protected function setUp(): void
19+
{
20+
if (!\in_array('ftp', stream_get_wrappers(), true) || !\ini_get('allow_url_fopen')) {
21+
$this->markTestSkipped('Unsupported stream "ftp".');
22+
}
23+
}
24+
1825
/**
1926
* @group network
2027
*/
2128
public function testRewindOnFtp()
2229
{
23-
try {
24-
$i = new RecursiveDirectoryIterator('ftp://speedtest:speedtest@ftp.otenet.gr/', \RecursiveDirectoryIterator::SKIP_DOTS);
25-
} catch (\UnexpectedValueException $e) {
26-
$this->markTestSkipped('Unsupported stream "ftp".');
27-
}
30+
$i = new RecursiveDirectoryIterator('ftp://speedtest:speedtest@ftp.otenet.gr/', \RecursiveDirectoryIterator::SKIP_DOTS);
2831

2932
$i->rewind();
3033

@@ -36,11 +39,7 @@ public function testRewindOnFtp()
3639
*/
3740
public function testSeekOnFtp()
3841
{
39-
try {
40-
$i = new RecursiveDirectoryIterator('ftp://speedtest:speedtest@ftp.otenet.gr/', \RecursiveDirectoryIterator::SKIP_DOTS);
41-
} catch (\UnexpectedValueException $e) {
42-
$this->markTestSkipped('Unsupported stream "ftp".');
43-
}
42+
$i = new RecursiveDirectoryIterator('ftp://speedtest:speedtest@ftp.otenet.gr/', \RecursiveDirectoryIterator::SKIP_DOTS);
4443

4544
$contains = [
4645
'ftp://speedtest:speedtest@ftp.otenet.gr'.\DIRECTORY_SEPARATOR.'test100Mb.db',

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy