[Finder] added Finder::ignoreUnreadableDirs() method

This commit is contained in:
Jean-François Simon 2013-03-29 15:47:08 +01:00
parent ea79360fba
commit a63b30b5ce
7 changed files with 87 additions and 10 deletions

View File

@ -33,6 +33,7 @@ abstract class AbstractAdapter implements AdapterInterface
protected $sort = false;
protected $paths = array();
protected $notPaths = array();
protected $ignoreUnreadableDirs = false;
private static $areSupported = array();
@ -210,6 +211,16 @@ abstract class AbstractAdapter implements AdapterInterface
return $this;
}
/**
* {@inheritdoc}
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (Boolean) $ignore;
return $this;
}
/**
* Returns whether the adapter is supported in the current environment.
*

View File

@ -92,9 +92,12 @@ abstract class AbstractFindAdapter extends AbstractAdapter
$this->buildSorting($command, $this->sort);
}
$command->setErrorHandler(function ($stderr) {
throw new AccessDeniedException($stderr);
});
$command->setErrorHandler(
$this->ignoreUnreadableDirs
// If directory is unreadable and finder is set to ignore it, `stderr` is ignored.
? function ($stderr) { return; }
: function ($stderr) { throw new AccessDeniedException($stderr); }
);
$paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
$iterator = new Iterator\FilePathsIterator($paths, $dir);

View File

@ -114,6 +114,13 @@ interface AdapterInterface
*/
public function setNotPath(array $notPaths);
/**
* @param boolean $ignore
*
* @return AdapterInterface Current instance
*/
public function ignoreUnreadableDirs($ignore = true);
/**
* @param string $dir
*

View File

@ -32,7 +32,7 @@ class PhpAdapter extends AbstractAdapter
}
$iterator = new \RecursiveIteratorIterator(
new Iterator\RecursiveDirectoryIterator($dir, $flags),
new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs),
\RecursiveIteratorIterator::SELF_FIRST
);

View File

@ -55,6 +55,7 @@ class Finder implements \IteratorAggregate, \Countable
private $adapters = array();
private $paths = array();
private $notPaths = array();
private $ignoreUnreadableDirs = false;
private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
@ -626,6 +627,22 @@ class Finder implements \IteratorAggregate, \Countable
return $this;
}
/**
* Tells finder to ignore unreadable directories.
*
* By default, scanning unreadable directories content throws an AccessDeniedException.
*
* @param boolean $ignore
*
* @return Finder The current Finder instance
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (Boolean) $ignore;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
@ -794,7 +811,8 @@ class Finder implements \IteratorAggregate, \Countable
->setFilters($this->filters)
->setSort($this->sort)
->setPath($this->paths)
->setNotPath($this->notPaths);
->setNotPath($this->notPaths)
->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
}
/**

View File

@ -21,13 +21,28 @@ use Symfony\Component\Finder\SplFileInfo;
*/
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
public function __construct($path, $flags)
/**
* @var boolean
*/
private $ignoreUnreadableDirs;
/**
* Constructor.
*
* @param string $path
* @param int $flags
* @param boolean $ignoreUnreadableDirs
*
* @throws \RuntimeException
*/
public function __construct($path, $flags, $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
}
parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
}
/**
@ -41,7 +56,7 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
}
/**
* @return mixed object
* @return \RecursiveIterator
*
* @throws AccessDeniedException
*/
@ -50,7 +65,12 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
try {
return parent::getChildren();
} catch (\UnexpectedValueException $e) {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
if ($this->ignoreUnreadableDirs) {
// If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
return new \RecursiveArrayIterator(array());
} else {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
}
}

View File

@ -725,16 +725,34 @@ class FinderTest extends Iterator\RealIteratorTestCase
$finder = $this->buildFinder($adapter);
$finder->files()->in(self::$tmpDir);
// make 'foo' directory non-openable
// make 'foo' directory non-readable
chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0333);
try {
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->getIterator());
$this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator());
$this->fail('Finder should throw an exception when opening a non-readable directory.');
} catch (\Exception $e) {
$this->assertEquals('Symfony\\Component\\Finder\\Exception\\AccessDeniedException', get_class($e));
}
// restore original permissions
chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0777);
}
/**
* @dataProvider getAdaptersTestData
*/
public function testIgnoredAccessDeniedException(Adapter\AdapterInterface $adapter)
{
$finder = $this->buildFinder($adapter);
$finder->files()->ignoreUnreadableDirs()->in(self::$tmpDir);
// make 'foo' directory non-readable
chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0333);
$this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator());
// restore original permissions
chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0777);
}