[Finder] Optimize the hot-path
This commit is contained in:
parent
4a9676b5a2
commit
f156de6b71
|
@ -18,8 +18,10 @@ namespace Symfony\Component\Finder\Iterator;
|
|||
*/
|
||||
class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator
|
||||
{
|
||||
private $iterator;
|
||||
private $isRecursive;
|
||||
private $patterns;
|
||||
private $excludedDirs = array();
|
||||
private $excludedPattern;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -29,10 +31,18 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
|
|||
*/
|
||||
public function __construct(\Iterator $iterator, array $directories)
|
||||
{
|
||||
$this->iterator = $iterator;
|
||||
$this->isRecursive = $iterator instanceof \RecursiveIterator;
|
||||
$this->patterns = array();
|
||||
$patterns = array();
|
||||
foreach ($directories as $directory) {
|
||||
$this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#';
|
||||
if (!$this->isRecursive || false !== strpos($directory, '/')) {
|
||||
$patterns[] = preg_quote($directory, '#');
|
||||
} else {
|
||||
$this->excludedDirs[$directory] = true;
|
||||
}
|
||||
}
|
||||
if ($patterns) {
|
||||
$this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
|
||||
}
|
||||
|
||||
parent::__construct($iterator);
|
||||
|
@ -45,12 +55,15 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
|
|||
*/
|
||||
public function accept()
|
||||
{
|
||||
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
|
||||
$path = str_replace('\\', '/', $path);
|
||||
foreach ($this->patterns as $pattern) {
|
||||
if (preg_match($pattern, $path)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->excludedPattern) {
|
||||
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
|
||||
$path = str_replace('\\', '/', $path);
|
||||
|
||||
return !preg_match($this->excludedPattern, $path);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -58,13 +71,14 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
|
|||
|
||||
public function hasChildren()
|
||||
{
|
||||
return $this->isRecursive && $this->getInnerIterator()->hasChildren();
|
||||
return $this->isRecursive && $this->iterator->hasChildren();
|
||||
}
|
||||
|
||||
public function getChildren()
|
||||
{
|
||||
$children = new self($this->getInnerIterator()->getChildren(), array());
|
||||
$children->patterns = $this->patterns;
|
||||
$children = new self($this->iterator->getChildren(), array());
|
||||
$children->excludedDirs = $this->excludedDirs;
|
||||
$children->excludedPattern = $this->excludedPattern;
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,11 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
|||
*/
|
||||
private $rewindable;
|
||||
|
||||
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
|
||||
private $rootPath;
|
||||
private $subPath;
|
||||
private $directorySeparator = '/';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
@ -48,6 +53,10 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
|||
|
||||
parent::__construct($path, $flags);
|
||||
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
|
||||
$this->rootPath = (string) $path;
|
||||
if ('/' !== DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
|
||||
$this->directorySeparator = DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +66,17 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
|||
*/
|
||||
public function current()
|
||||
{
|
||||
return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
|
||||
// the logic here avoids redoing the same work in all iterations
|
||||
|
||||
if (null === $subPathname = $this->subPath) {
|
||||
$subPathname = $this->subPath = (string) $this->getSubPath();
|
||||
}
|
||||
if ('' !== $subPathname) {
|
||||
$subPathname .= $this->directorySeparator;
|
||||
}
|
||||
$subPathname .= $this->getFilename();
|
||||
|
||||
return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,6 +92,10 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
|||
if ($children instanceof self) {
|
||||
// parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
|
||||
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
|
||||
|
||||
// performance optimization to avoid redoing the same work in all children
|
||||
$children->rewindable = &$this->rewindable;
|
||||
$children->rootPath = $this->rootPath;
|
||||
}
|
||||
|
||||
return $children;
|
||||
|
|
Reference in New Issue