8000 [Finder] Optimize the hot-path · symfony/symfony@f156de6 · GitHub
[go: up one dir, main page]

Skip to content

Commit f156de6

Browse files
[Finder] Optimize the hot-path
1 parent 4a9676b commit f156de6

File tree

2 files changed

+50
-13
lines changed

2 files changed

+50
-13
lines changed

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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
*/
1919
class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator
2020
{
21+
private $iterator;
2122
private $isRecursive;
22-
private $patterns;
23+
private $excludedDirs = array();
24+
private $excludedPattern;
2325

2426
/**
2527
* Constructor.
@@ -29,10 +31,18 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
2931
*/
3032
public function __construct(\Iterator $iterator, array $directories)
3133
{
34+
$this->iterator = $iterator;
3235
$this->isRecursive = $iterator instanceof \RecursiveIterator;
33-
$this->patterns = array();
36+
$patterns = array();
3437
foreach ($directories as $directory) {
35-
$this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#';
38+
if (!$this->isRecursive || false !== strpos($directory, '/')) {
39+
$patterns[] = preg_quote($directory, '#');
40+
} else {
41+
$this->excludedDirs[$directory] = true;
42+
}
43+
}
44+
if ($patterns) {
45+
$this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
3646
}
3747

3848
parent::__construct($iterator);
@@ -45,26 +55,30 @@ public function __construct(\Iterator $iterator, array $directories)
4555
*/
4656
public function accept()
4757
{
48-
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
49-
$path = str_replace('\\', '/', $path);
50-
foreach ($this->patterns as $pattern) {
51-
if (preg_match($pattern, $path)) {
52-
return false;
53-
}
58+
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
59+
return false;
60+
}
61+
62+
if ($this->excludedPattern) {
63+
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
64+
$path = str_replace('\\', '/', $path);
65+
66+
return !preg_match($this->excludedPattern, $path);
5467
}
5568

5669
return true;
5770
}
5871

5972
public function hasChildren()
6073
{
61-
return $this->isRecursive && $this->getInnerIterator()->hasChildren();
74+
return $this->isRecursive && $this->iterator->hasChildren();
6275
}
6376

6477
public function getChildren()
6578
{
66-
$children = new self($this->getInnerIterator()->getChildren(), array());
67-
$children->patterns = $this->patterns;
79+
$children = new self($this->iterator->getChildren(), array());
80+
$children->excludedDirs = $this->excludedDirs;
81+
$children->excludedPattern = $this->excludedPattern;
6882

6983
return $children;
7084
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
3131
*/
3232
private $rewindable;
3333

34+
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
35+
private $rootPath;
36+
private $subPath;
37+
private $directorySeparator = '/';
38+
3439
/**
3540
* Constructor.
3641
*
@@ -48,6 +53,10 @@ public function __construct($path, $flags, $ignoreUnreadableDirs = false)
4853

4954
parent::__construct($path, $flags);
5055
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
56+
$this->rootPath = (string) $path;
57+
if ('/' !== DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
58+
$this->directorySeparator = DIRECTORY_SEPARATOR;
59+
}
5160
}
5261

5362
/**
@@ -57,7 +66,17 @@ public function __construct($path, $flags, $ignoreUnreadableDirs = false)
5766
*/
5867
public function current()
5968
{
60-
return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
69+
// the logic here avoids redoing the same work in all iterations
70+
71+
if (null === $subPathname = $this->subPath) {
72+
$subPathname = $this->subPath = (string) $this->getSubPath();
73+
}
74+
if ('' !== $subPathname) {
75+
$subPathname .= $this->directorySeparator;
76+
}
77+
$subPathname .= $this->getFilename();
78+
79+
return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
6180
}
6281

6382
/**
@@ -73,6 +92,10 @@ public function getChildren()
7392
if ($children instanceof self) {
7493
// parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
7594
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
95+
96+
// performance optimization to avoid redoing the same work in all children
97+
$children->rewindable = &$this->rewindable;
98+
$children->rootPath = $this->rootPath;
7699
}
77100

78101
return $children;

0 commit comments

Comments
 (0)
0