2010-04-21 08:31:18 +01:00
< ? php
/*
2011-01-15 13:29:43 +00:00
* This file is part of the Symfony package .
2010-04-21 08:31:18 +01:00
*
2011-03-06 11:40:06 +00:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2010-04-21 08:31:18 +01:00
*
2011-01-15 13:29:43 +00:00
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
2010-04-21 08:31:18 +01:00
*/
2011-01-15 13:29:43 +00:00
namespace Symfony\Component\Finder ;
2014-12-21 16:36:15 +00:00
use Symfony\Component\Finder\Comparator\DateComparator ;
use Symfony\Component\Finder\Comparator\NumberComparator ;
2019-03-28 11:16:35 +00:00
use Symfony\Component\Finder\Exception\DirectoryNotFoundException ;
2014-12-21 16:36:15 +00:00
use Symfony\Component\Finder\Iterator\CustomFilterIterator ;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator ;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator ;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator ;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator ;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator ;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator ;
use Symfony\Component\Finder\Iterator\SortableIterator ;
2012-04-21 20:31:44 +01:00
2010-04-21 08:31:18 +01:00
/**
* Finder allows to build rules to find files and directories .
*
* It is a thin wrapper around several specialized iterator classes .
*
2010-05-21 12:22:34 +01:00
* All rules may be invoked several times .
2010-04-21 08:31:18 +01:00
*
2019-02-22 13:34:02 +00:00
* All methods return the current Finder object to allow chaining :
2010-04-21 08:31:18 +01:00
*
2018-08-28 22:39:52 +01:00
* $finder = Finder :: create () -> files () -> name ( '*.php' ) -> in ( __DIR__ );
2010-04-21 08:31:18 +01:00
*
2011-03-06 11:40:06 +00:00
* @ author Fabien Potencier < fabien @ symfony . com >
2010-04-21 08:31:18 +01:00
*/
2012-04-22 15:13:31 +01:00
class Finder implements \IteratorAggregate , \Countable
2010-04-21 08:31:18 +01:00
{
2011-05-23 09:45:59 +01:00
const IGNORE_VCS_FILES = 1 ;
const IGNORE_DOT_FILES = 2 ;
2019-03-05 00:59:42 +00:00
const IGNORE_VCS_IGNORED_FILES = 4 ;
2011-05-23 09:45:59 +01:00
2014-10-22 19:27:13 +01:00
private $mode = 0 ;
2019-01-16 09:39:14 +00:00
private $names = [];
private $notNames = [];
private $exclude = [];
private $filters = [];
private $depths = [];
private $sizes = [];
2011-03-08 19:54:22 +00:00
private $followLinks = false ;
2018-07-16 15:10:04 +01:00
private $reverseSorting = false ;
2014-10-22 19:27:13 +01:00
private $sort = false ;
private $ignore = 0 ;
2019-01-16 09:39:14 +00:00
private $dirs = [];
private $dates = [];
private $iterators = [];
private $contains = [];
private $notContains = [];
private $paths = [];
private $notPaths = [];
2013-03-29 14:47:08 +00:00
private $ignoreUnreadableDirs = false ;
2010-05-06 12:25:53 +01:00
2019-01-16 09:39:14 +00:00
private static $vcsPatterns = [ '.svn' , '_svn' , 'CVS' , '_darcs' , '.arch-params' , '.monotone' , '.bzr' , '.git' , '.hg' ];
2011-05-23 09:45:59 +01:00
public function __construct ()
{
$this -> ignore = static :: IGNORE_VCS_FILES | static :: IGNORE_DOT_FILES ;
}
2011-06-14 13:17:22 +01:00
/**
* Creates a new Finder .
*
2016-12-26 07:50:27 +00:00
* @ return static
2011-06-14 13:17:22 +01:00
*/
2012-07-09 13:50:58 +01:00
public static function create ()
2011-06-14 13:17:22 +01:00
{
2012-07-20 10:54:42 +01:00
return new static ();
2011-06-14 13:17:22 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Restricts the matching to directories only .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*/
public function directories ()
2010-04-21 08:31:18 +01:00
{
2010-05-06 12:25:53 +01:00
$this -> mode = Iterator\FileTypeFilterIterator :: ONLY_DIRECTORIES ;
return $this ;
2010-05-04 10:34:29 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Restricts the matching to files only .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*/
public function files ()
2010-05-04 10:34:29 +01:00
{
2010-05-06 12:25:53 +01:00
$this -> mode = Iterator\FileTypeFilterIterator :: ONLY_FILES ;
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
2010-05-21 12:22:34 +01:00
* Adds tests for the directory depth .
2010-05-06 12:25:53 +01:00
*
2010-05-21 12:22:34 +01:00
* Usage :
2010-05-06 12:25:53 +01:00
*
2018-08-28 22:39:52 +01:00
* $finder -> depth ( '> 1' ) // the Finder will start matching at level 1.
* $finder -> depth ( '< 3' ) // the Finder will descend at most 3 levels of directories below the starting point.
2018-09-02 18:48:09 +01:00
* $finder -> depth ([ '>= 1' , '< 3' ])
2010-05-06 12:25:53 +01:00
*
2018-07-07 16:28:15 +01:00
* @ param string | int | string [] | int [] $levels The depth level expression or an array of depth levels
2010-05-06 12:25:53 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see DepthRangeFilterIterator
* @ see NumberComparator
2010-05-06 12:25:53 +01:00
*/
2018-07-07 16:28:15 +01:00
public function depth ( $levels )
2010-05-04 10:34:29 +01:00
{
2018-07-07 16:28:15 +01:00
foreach (( array ) $levels as $level ) {
$this -> depths [] = new Comparator\NumberComparator ( $level );
}
2010-05-14 08:29:04 +01:00
return $this ;
}
/**
2010-05-21 11:50:13 +01:00
* Adds tests for file dates ( last modified ) .
2010-05-14 08:29:04 +01:00
*
* The date must be something that strtotime () is able to parse :
*
2018-08-28 22:39:52 +01:00
* $finder -> date ( 'since yesterday' );
* $finder -> date ( 'until 2 days ago' );
* $finder -> date ( '> now - 2 hours' );
* $finder -> date ( '>= 2005-10-15' );
2018-09-02 18:48:09 +01:00
* $finder -> date ([ '>= 2005-10-15' , '<= 2006-05-27' ]);
2010-05-14 08:29:04 +01:00
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $dates A date range string or an array of date ranges
2010-05-14 08:29:04 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-14 08:29:04 +01:00
*
2010-05-21 11:50:13 +01:00
* @ see strtotime
2014-12-21 16:36:15 +00:00
* @ see DateRangeFilterIterator
* @ see DateComparator
2010-05-14 08:29:04 +01:00
*/
2018-07-07 16:28:15 +01:00
public function date ( $dates )
2010-05-14 08:29:04 +01:00
{
2018-07-07 16:28:15 +01:00
foreach (( array ) $dates as $date ) {
$this -> dates [] = new Comparator\DateComparator ( $date );
}
2010-05-06 12:25:53 +01:00
return $this ;
2010-05-04 10:34:29 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Adds rules that files must match .
*
* You can use patterns ( delimited with / sign ), globs or simple strings .
*
2018-08-28 22:39:52 +01:00
* $finder -> name ( '*.php' )
* $finder -> name ( '/\.php$/' ) // same as above
* $finder -> name ( 'test.php' )
2018-09-05 18:51:25 +01:00
* $finder -> name ([ 'test.py' , 'test.php' ])
2010-05-06 12:25:53 +01:00
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $patterns A pattern ( a regexp , a glob , or a string ) or an array of patterns
2010-05-06 12:25:53 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see FilenameFilterIterator
2010-05-06 12:25:53 +01:00
*/
2018-07-07 16:28:15 +01:00
public function name ( $patterns )
2010-04-21 08:31:18 +01:00
{
2019-06-13 11:57:15 +01:00
$this -> names = array_merge ( $this -> names , ( array ) $patterns );
2010-05-06 12:25:53 +01:00
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Adds rules that files must not match .
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $patterns A pattern ( a regexp , a glob , or a string ) or an array of patterns
2010-05-06 12:25:53 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see FilenameFilterIterator
2010-05-06 12:25:53 +01:00
*/
2018-07-07 16:28:15 +01:00
public function notName ( $patterns )
2010-05-06 12:25:53 +01:00
{
2019-06-13 11:57:15 +01:00
$this -> notNames = array_merge ( $this -> notNames , ( array ) $patterns );
2010-05-04 10:34:29 +01:00
2010-05-06 12:25:53 +01:00
return $this ;
}
2010-04-21 08:31:18 +01:00
2012-04-19 16:34:50 +01:00
/**
* Adds tests that file contents must match .
*
* Strings or PCRE patterns can be used :
*
2018-08-28 22:39:52 +01:00
* $finder -> contains ( 'Lorem ipsum' )
* $finder -> contains ( '/Lorem ipsum/i' )
2018-09-05 18:51:25 +01:00
* $finder -> contains ([ 'dolor' , '/ipsum/i' ])
2012-04-19 16:34:50 +01:00
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $patterns A pattern ( string or regexp ) or an array of patterns
2012-04-19 16:34:50 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-04-19 16:34:50 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see FilecontentFilterIterator
2012-04-19 16:34:50 +01:00
*/
2018-07-07 16:28:15 +01:00
public function contains ( $patterns )
2012-04-19 16:34:50 +01:00
{
2019-06-13 11:57:15 +01:00
$this -> contains = array_merge ( $this -> contains , ( array ) $patterns );
2012-04-19 16:34:50 +01:00
return $this ;
}
/**
* Adds tests that file contents must not match .
*
* Strings or PCRE patterns can be used :
*
2018-08-28 22:39:52 +01:00
* $finder -> notContains ( 'Lorem ipsum' )
* $finder -> notContains ( '/Lorem ipsum/i' )
2018-09-05 18:51:25 +01:00
* $finder -> notContains ([ 'lorem' , '/dolor/i' ])
2012-04-19 16:34:50 +01:00
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $patterns A pattern ( string or regexp ) or an array of patterns
2012-04-19 16:34:50 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-04-19 16:34:50 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see FilecontentFilterIterator
2012-04-19 16:34:50 +01:00
*/
2018-07-07 16:28:15 +01:00
public function notContains ( $patterns )
2012-04-19 16:34:50 +01:00
{
2019-06-13 11:57:15 +01:00
$this -> notContains = array_merge ( $this -> notContains , ( array ) $patterns );
2012-04-19 16:34:50 +01:00
return $this ;
}
2012-07-04 07:13:41 +01:00
/**
* Adds rules that filenames must match .
*
* You can use patterns ( delimited with / sign ) or simple strings .
*
2018-08-28 22:39:52 +01:00
* $finder -> path ( 'some/special/dir' )
* $finder -> path ( '/some\/special\/dir/' ) // same as above
2018-09-05 18:51:25 +01:00
* $finder -> path ([ 'some dir' , 'another/dir' ])
2012-07-04 07:13:41 +01:00
*
* Use only / as dirname separator .
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $patterns A pattern ( a regexp or a string ) or an array of patterns
2012-07-04 07:13:41 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-07-04 07:13:41 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see FilenameFilterIterator
2012-07-04 07:13:41 +01:00
*/
2018-07-07 16:28:15 +01:00
public function path ( $patterns )
2012-07-04 07:13:41 +01:00
{
2019-06-13 11:57:15 +01:00
$this -> paths = array_merge ( $this -> paths , ( array ) $patterns );
2012-07-04 07:13:41 +01:00
return $this ;
}
/**
* Adds rules that filenames must not match .
*
* You can use patterns ( delimited with / sign ) or simple strings .
*
2018-08-28 22:39:52 +01:00
* $finder -> notPath ( 'some/special/dir' )
* $finder -> notPath ( '/some\/special\/dir/' ) // same as above
2018-09-05 18:51:25 +01:00
* $finder -> notPath ([ 'some/file.txt' , 'another/file.log' ])
2012-07-04 07:13:41 +01:00
*
* Use only / as dirname separator .
*
2018-07-07 16:28:15 +01:00
* @ param string | string [] $patterns A pattern ( a regexp or a string ) or an array of patterns
2012-07-04 07:13:41 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-07-04 07:13:41 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see FilenameFilterIterator
2012-07-04 07:13:41 +01:00
*/
2018-07-07 16:28:15 +01:00
public function notPath ( $patterns )
2012-07-04 07:13:41 +01:00
{
2019-06-13 11:57:15 +01:00
$this -> notPaths = array_merge ( $this -> notPaths , ( array ) $patterns );
2012-07-04 07:13:41 +01:00
return $this ;
}
2010-05-06 12:25:53 +01:00
/**
* Adds tests for file sizes .
*
2018-08-28 22:39:52 +01:00
* $finder -> size ( '> 10K' );
* $finder -> size ( '<= 1Ki' );
* $finder -> size ( 4 );
2018-09-05 18:51:25 +01:00
* $finder -> size ([ '> 10K' , '< 20K' ])
2010-05-06 12:25:53 +01:00
*
2018-07-07 16:28:15 +01:00
* @ param string | int | string [] | int [] $sizes A size range string or an integer or an array of size ranges
2010-05-06 12:25:53 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SizeRangeFilterIterator
* @ see NumberComparator
2010-05-06 12:25:53 +01:00
*/
2018-07-07 16:28:15 +01:00
public function size ( $sizes )
2010-04-21 08:31:18 +01:00
{
2018-07-07 16:28:15 +01:00
foreach (( array ) $sizes as $size ) {
$this -> sizes [] = new Comparator\NumberComparator ( $size );
}
2010-05-06 12:25:53 +01:00
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Excludes directories .
*
2018-03-04 15:13:29 +00:00
* Directories passed as argument must be relative to the ones defined with the `in()` method . For example :
*
* $finder -> in ( __DIR__ ) -> exclude ( 'ruby' );
*
2012-05-15 21:19:31 +01:00
* @ param string | array $dirs A directory path or an array of directories
2010-05-06 12:25:53 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see ExcludeDirectoryFilterIterator
2010-05-06 12:25:53 +01:00
*/
2011-08-09 17:23:43 +01:00
public function exclude ( $dirs )
2010-05-06 12:25:53 +01:00
{
2011-08-09 17:23:43 +01:00
$this -> exclude = array_merge ( $this -> exclude , ( array ) $dirs );
2010-04-21 08:31:18 +01:00
2010-05-06 12:25:53 +01:00
return $this ;
}
2011-05-23 09:45:59 +01:00
/**
* Excludes " hidden " directories and files ( starting with a dot ) .
*
2018-02-10 17:16:30 +00:00
* This option is enabled by default .
*
2014-11-30 13:33:44 +00:00
* @ param bool $ignoreDotFiles Whether to exclude " hidden " files or not
2011-05-23 09:45:59 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2011-05-23 09:45:59 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see ExcludeDirectoryFilterIterator
2011-05-23 09:45:59 +01:00
*/
public function ignoreDotFiles ( $ignoreDotFiles )
{
if ( $ignoreDotFiles ) {
2015-03-07 19:12:23 +00:00
$this -> ignore |= static :: IGNORE_DOT_FILES ;
2011-05-23 09:45:59 +01:00
} else {
2015-03-07 19:12:23 +00:00
$this -> ignore &= ~ static :: IGNORE_DOT_FILES ;
2011-05-23 09:45:59 +01:00
}
return $this ;
}
2010-05-06 12:25:53 +01:00
/**
* Forces the finder to ignore version control directories .
*
2018-02-10 17:16:30 +00:00
* This option is enabled by default .
*
2014-11-30 13:33:44 +00:00
* @ param bool $ignoreVCS Whether to exclude VCS files or not
2011-05-23 09:45:59 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see ExcludeDirectoryFilterIterator
2010-05-06 12:25:53 +01:00
*/
public function ignoreVCS ( $ignoreVCS )
2010-04-21 08:31:18 +01:00
{
2011-05-23 09:45:59 +01:00
if ( $ignoreVCS ) {
2015-03-07 19:12:23 +00:00
$this -> ignore |= static :: IGNORE_VCS_FILES ;
2011-05-23 09:45:59 +01:00
} else {
2015-03-07 19:12:23 +00:00
$this -> ignore &= ~ static :: IGNORE_VCS_FILES ;
2011-05-23 09:45:59 +01:00
}
2010-05-06 12:25:53 +01:00
return $this ;
2010-04-21 08:31:18 +01:00
}
2019-03-05 00:59:42 +00:00
/**
* Forces Finder to obey . gitignore and ignore files based on rules listed there .
*
* This option is disabled by default .
*
* @ return $this
*/
public function ignoreVCSIgnored ( bool $ignoreVCSIgnored )
{
if ( $ignoreVCSIgnored ) {
$this -> ignore |= static :: IGNORE_VCS_IGNORED_FILES ;
} else {
$this -> ignore &= ~ static :: IGNORE_VCS_IGNORED_FILES ;
}
return $this ;
}
2012-12-10 08:50:12 +00:00
/**
* Adds VCS patterns .
*
2014-12-21 16:36:15 +00:00
* @ see ignoreVCS ()
2012-12-10 08:50:12 +00:00
*
* @ param string | string [] $pattern VCS patterns to ignore
*/
2012-07-09 13:50:58 +01:00
public static function addVCSPattern ( $pattern )
2011-05-23 09:45:59 +01:00
{
2012-12-10 08:50:12 +00:00
foreach (( array ) $pattern as $p ) {
self :: $vcsPatterns [] = $p ;
}
self :: $vcsPatterns = array_unique ( self :: $vcsPatterns );
2011-05-23 09:45:59 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Sorts files and directories by an anonymous function .
*
* The anonymous function receives two \SplFileInfo instances to compare .
*
* This can be slow as all the matching files and directories must be retrieved for comparison .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SortableIterator
2010-05-06 12:25:53 +01:00
*/
public function sort ( \Closure $closure )
2010-04-21 08:31:18 +01:00
{
2010-05-06 12:25:53 +01:00
$this -> sort = $closure ;
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Sorts files and directories by name .
*
* This can be slow as all the matching files and directories must be retrieved for comparison .
*
2018-04-24 12:54:22 +01:00
* @ param bool $useNaturalSort Whether to use natural sort or not , disabled by default
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SortableIterator
2010-05-06 12:25:53 +01:00
*/
2018-04-24 12:54:22 +01:00
public function sortByName ( /* bool $useNaturalSort = false */ )
2010-04-21 08:31:18 +01:00
{
2018-08-29 10:05:16 +01:00
if ( \func_num_args () < 1 && __CLASS__ !== \get_class ( $this ) && __CLASS__ !== ( new \ReflectionMethod ( $this , __FUNCTION__ )) -> getDeclaringClass () -> getName () && ! $this instanceof \PHPUnit\Framework\MockObject\MockObject && ! $this instanceof \Prophecy\Prophecy\ProphecySubjectInterface ) {
@ trigger_error ( sprintf ( 'The "%s()" method will have a new "bool $useNaturalSort = false" argument in version 5.0, not defining it is deprecated since Symfony 4.2.' , __METHOD__ ), E_USER_DEPRECATED );
}
2018-07-26 09:59:12 +01:00
$useNaturalSort = 0 < \func_num_args () && func_get_arg ( 0 );
2018-04-24 12:54:22 +01:00
$this -> sort = $useNaturalSort ? Iterator\SortableIterator :: SORT_BY_NAME_NATURAL : Iterator\SortableIterator :: SORT_BY_NAME ;
2010-05-06 12:25:53 +01:00
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Sorts files and directories by type ( directories before files ), then by name .
*
* This can be slow as all the matching files and directories must be retrieved for comparison .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SortableIterator
2010-05-06 12:25:53 +01:00
*/
public function sortByType ()
2010-04-21 08:31:18 +01:00
{
2010-05-06 12:25:53 +01:00
$this -> sort = Iterator\SortableIterator :: SORT_BY_TYPE ;
2012-03-31 19:44:50 +01:00
return $this ;
}
/**
* Sorts files and directories by the last accessed time .
*
* This is the time that the file was last accessed , read or written to .
*
* This can be slow as all the matching files and directories must be retrieved for comparison .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-03-31 19:44:50 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SortableIterator
2012-03-31 19:44:50 +01:00
*/
public function sortByAccessedTime ()
{
$this -> sort = Iterator\SortableIterator :: SORT_BY_ACCESSED_TIME ;
return $this ;
}
2018-07-16 15:10:04 +01:00
/**
* Reverses the sorting .
*
* @ return $this
*/
public function reverseSorting ()
{
$this -> reverseSorting = true ;
return $this ;
}
2012-03-31 19:44:50 +01:00
/**
* Sorts files and directories by the last inode changed time .
*
* This is the time that the inode information was last modified ( permissions , owner , group or other metadata ) .
*
* On Windows , since inode is not available , changed time is actually the file creation time .
*
* This can be slow as all the matching files and directories must be retrieved for comparison .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-03-31 19:44:50 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SortableIterator
2012-03-31 19:44:50 +01:00
*/
public function sortByChangedTime ()
{
$this -> sort = Iterator\SortableIterator :: SORT_BY_CHANGED_TIME ;
return $this ;
}
/**
* Sorts files and directories by the last modified time .
*
* This is the last time the actual contents of the file were last modified .
*
* This can be slow as all the matching files and directories must be retrieved for comparison .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-03-31 19:44:50 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see SortableIterator
2012-03-31 19:44:50 +01:00
*/
public function sortByModifiedTime ()
{
$this -> sort = Iterator\SortableIterator :: SORT_BY_MODIFIED_TIME ;
2010-05-06 12:25:53 +01:00
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Filters the iterator with an anonymous function .
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2014-12-21 16:36:15 +00:00
* @ see CustomFilterIterator
2010-05-06 12:25:53 +01:00
*/
public function filter ( \Closure $closure )
2010-04-21 08:31:18 +01:00
{
2010-05-06 12:25:53 +01:00
$this -> filters [] = $closure ;
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Forces the following of symlinks .
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*/
public function followLinks ()
2010-04-21 08:31:18 +01:00
{
2010-05-06 12:25:53 +01:00
$this -> followLinks = true ;
return $this ;
2010-04-21 08:31:18 +01:00
}
2013-03-29 14:47:08 +00:00
/**
* Tells finder to ignore unreadable directories .
*
* By default , scanning unreadable directories content throws an AccessDeniedException .
*
2014-11-30 13:33:44 +00:00
* @ param bool $ignore
2013-03-29 14:47:08 +00:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2013-03-29 14:47:08 +00:00
*/
public function ignoreUnreadableDirs ( $ignore = true )
{
2014-04-12 18:44:00 +01:00
$this -> ignoreUnreadableDirs = ( bool ) $ignore ;
2013-03-29 14:47:08 +00:00
return $this ;
}
2010-05-06 12:25:53 +01:00
/**
* Searches files and directories which match defined rules .
*
2019-06-28 09:02:59 +01:00
* @ param string | string [] $dirs A directory path or an array of directories
2010-05-06 12:25:53 +01:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2010-05-06 12:25:53 +01:00
*
2019-03-28 11:16:35 +00:00
* @ throws DirectoryNotFoundException if one of the directories does not exist
2010-05-06 12:25:53 +01:00
*/
public function in ( $dirs )
2010-04-21 08:31:18 +01:00
{
2019-01-16 09:39:14 +00:00
$resolvedDirs = [];
2013-01-02 00:06:26 +00:00
foreach (( array ) $dirs as $dir ) {
if ( is_dir ( $dir )) {
2018-04-03 12:22:20 +01:00
$resolvedDirs [] = $this -> normalizeDir ( $dir );
2019-10-16 11:30:11 +01:00
} elseif ( $glob = glob ( $dir , ( \defined ( 'GLOB_BRACE' ) ? GLOB_BRACE : 0 ) | GLOB_ONLYDIR | GLOB_NOSORT )) {
sort ( $glob );
2019-01-16 09:39:14 +00:00
$resolvedDirs = array_merge ( $resolvedDirs , array_map ([ $this , 'normalizeDir' ], $glob ));
2013-01-02 00:06:26 +00:00
} else {
2019-03-28 11:16:35 +00:00
throw new DirectoryNotFoundException ( sprintf ( 'The "%s" directory does not exist.' , $dir ));
2010-05-06 12:25:53 +01:00
}
}
2013-01-02 00:06:26 +00:00
$this -> dirs = array_merge ( $this -> dirs , $resolvedDirs );
2010-05-06 12:25:53 +01:00
return $this ;
2010-04-21 08:31:18 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Returns an Iterator for the current Finder configuration .
*
* This method implements the IteratorAggregate interface .
*
2016-06-12 11:30:51 +01:00
* @ return \Iterator | SplFileInfo [] An iterator
2010-05-06 12:25:53 +01:00
*
* @ throws \LogicException if the in () method has not been called
*/
public function getIterator ()
2010-04-21 08:31:18 +01:00
{
2018-07-05 12:24:53 +01:00
if ( 0 === \count ( $this -> dirs ) && 0 === \count ( $this -> iterators )) {
2012-12-10 08:25:50 +00:00
throw new \LogicException ( 'You must call one of in() or append() methods before iterating over a Finder.' );
2010-05-06 12:25:53 +01:00
}
2018-07-05 12:24:53 +01:00
if ( 1 === \count ( $this -> dirs ) && 0 === \count ( $this -> iterators )) {
2010-05-06 12:25:53 +01:00
return $this -> searchInDirectory ( $this -> dirs [ 0 ]);
}
$iterator = new \AppendIterator ();
2010-05-07 15:09:11 +01:00
foreach ( $this -> dirs as $dir ) {
2010-05-06 12:25:53 +01:00
$iterator -> append ( $this -> searchInDirectory ( $dir ));
}
2011-03-25 14:50:47 +00:00
foreach ( $this -> iterators as $it ) {
$iterator -> append ( $it );
}
2010-05-06 12:25:53 +01:00
return $iterator ;
2010-04-21 08:31:18 +01:00
}
2011-03-25 14:50:47 +00:00
/**
* Appends an existing set of files / directories to the finder .
*
* The set can be another Finder , an Iterator , an IteratorAggregate , or even a plain array .
*
2018-08-30 09:00:46 +01:00
* @ param iterable $iterator
2012-12-10 08:25:50 +00:00
*
2016-12-26 07:50:27 +00:00
* @ return $this
2012-12-10 08:25:50 +00:00
*
2017-09-11 10:28:55 +01:00
* @ throws \InvalidArgumentException when the given argument is not iterable
2011-03-25 14:50:47 +00:00
*/
public function append ( $iterator )
{
if ( $iterator instanceof \IteratorAggregate ) {
$this -> iterators [] = $iterator -> getIterator ();
} elseif ( $iterator instanceof \Iterator ) {
$this -> iterators [] = $iterator ;
2018-07-05 12:24:53 +01:00
} elseif ( $iterator instanceof \Traversable || \is_array ( $iterator )) {
2011-03-25 14:50:47 +00:00
$it = new \ArrayIterator ();
foreach ( $iterator as $file ) {
$it -> append ( $file instanceof \SplFileInfo ? $file : new \SplFileInfo ( $file ));
}
$this -> iterators [] = $it ;
} else {
throw new \InvalidArgumentException ( 'Finder::append() method wrong argument type.' );
}
2012-12-10 08:25:50 +00:00
return $this ;
2011-03-25 14:50:47 +00:00
}
2012-05-01 13:46:26 +01:00
2017-07-10 22:30:02 +01:00
/**
* Check if the any results were found .
*
* @ return bool
*/
public function hasResults ()
{
foreach ( $this -> getIterator () as $_ ) {
return true ;
}
return false ;
}
2012-04-22 15:13:31 +01:00
/**
* Counts all the results collected by the iterators .
*
* @ return int
*/
public function count ()
{
return iterator_count ( $this -> getIterator ());
}
2011-03-25 14:50:47 +00:00
2017-10-28 19:15:32 +01:00
private function searchInDirectory ( string $dir ) : \Iterator
2012-04-21 20:31:44 +01:00
{
2019-02-20 09:24:15 +00:00
$exclude = $this -> exclude ;
$notPaths = $this -> notPaths ;
2011-05-23 09:45:59 +01:00
if ( static :: IGNORE_VCS_FILES === ( static :: IGNORE_VCS_FILES & $this -> ignore )) {
2019-02-20 09:24:15 +00:00
$exclude = array_merge ( $exclude , self :: $vcsPatterns );
2010-05-06 12:25:53 +01:00
}
2011-05-23 09:45:59 +01:00
if ( static :: IGNORE_DOT_FILES === ( static :: IGNORE_DOT_FILES & $this -> ignore )) {
2019-02-20 09:24:15 +00:00
$notPaths [] = '#(^|/)\..+(/|$)#' ;
2011-05-23 09:45:59 +01:00
}
2019-03-05 00:59:42 +00:00
if ( static :: IGNORE_VCS_IGNORED_FILES === ( static :: IGNORE_VCS_IGNORED_FILES & $this -> ignore )) {
$gitignoreFilePath = sprintf ( '%s/.gitignore' , $dir );
if ( ! is_readable ( $gitignoreFilePath )) {
throw new \RuntimeException ( sprintf ( 'The "ignoreVCSIgnored" option cannot be used by the Finder as the "%s" file is not readable.' , $gitignoreFilePath ));
}
$notPaths = array_merge ( $notPaths , [ Gitignore :: toRegex ( file_get_contents ( $gitignoreFilePath ))]);
}
2015-09-15 09:24:28 +01:00
$minDepth = 0 ;
$maxDepth = PHP_INT_MAX ;
foreach ( $this -> depths as $comparator ) {
switch ( $comparator -> getOperator ()) {
case '>' :
$minDepth = $comparator -> getTarget () + 1 ;
break ;
case '>=' :
$minDepth = $comparator -> getTarget ();
break ;
case '<' :
$maxDepth = $comparator -> getTarget () - 1 ;
break ;
case '<=' :
$maxDepth = $comparator -> getTarget ();
break ;
default :
$minDepth = $maxDepth = $comparator -> getTarget ();
}
}
$flags = \RecursiveDirectoryIterator :: SKIP_DOTS ;
if ( $this -> followLinks ) {
$flags |= \RecursiveDirectoryIterator :: FOLLOW_SYMLINKS ;
}
$iterator = new Iterator\RecursiveDirectoryIterator ( $dir , $flags , $this -> ignoreUnreadableDirs );
2019-02-20 09:24:15 +00:00
if ( $exclude ) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator ( $iterator , $exclude );
2015-09-15 09:24:28 +01:00
}
$iterator = new \RecursiveIteratorIterator ( $iterator , \RecursiveIteratorIterator :: SELF_FIRST );
if ( $minDepth > 0 || $maxDepth < PHP_INT_MAX ) {
$iterator = new Iterator\DepthRangeFilterIterator ( $iterator , $minDepth , $maxDepth );
}
if ( $this -> mode ) {
$iterator = new Iterator\FileTypeFilterIterator ( $iterator , $this -> mode );
}
if ( $this -> names || $this -> notNames ) {
$iterator = new Iterator\FilenameFilterIterator ( $iterator , $this -> names , $this -> notNames );
}
if ( $this -> contains || $this -> notContains ) {
$iterator = new Iterator\FilecontentFilterIterator ( $iterator , $this -> contains , $this -> notContains );
}
if ( $this -> sizes ) {
$iterator = new Iterator\SizeRangeFilterIterator ( $iterator , $this -> sizes );
}
if ( $this -> dates ) {
$iterator = new Iterator\DateRangeFilterIterator ( $iterator , $this -> dates );
}
if ( $this -> filters ) {
$iterator = new Iterator\CustomFilterIterator ( $iterator , $this -> filters );
}
2019-02-20 09:24:15 +00:00
if ( $this -> paths || $notPaths ) {
$iterator = new Iterator\PathFilterIterator ( $iterator , $this -> paths , $notPaths );
2015-09-15 09:24:28 +01:00
}
2018-10-10 13:11:49 +01:00
if ( $this -> sort || $this -> reverseSorting ) {
$iteratorAggregate = new Iterator\SortableIterator ( $iterator , $this -> sort , $this -> reverseSorting );
2018-07-16 15:10:04 +01:00
$iterator = $iteratorAggregate -> getIterator ();
}
2015-09-15 09:24:28 +01:00
return $iterator ;
2012-04-21 20:31:44 +01:00
}
2010-05-06 12:25:53 +01:00
2018-04-03 12:22:20 +01:00
/**
* Normalizes given directory names by removing trailing slashes .
*
2018-09-26 11:57:02 +01:00
* Excluding : ( s ) ftp :// wrapper
*
2018-04-03 12:22:20 +01:00
* @ param string $dir
*
* @ return string
*/
private function normalizeDir ( $dir )
{
2018-09-26 11:57:02 +01:00
$dir = rtrim ( $dir , '/' . \DIRECTORY_SEPARATOR );
if ( preg_match ( '#^s?ftp://#' , $dir )) {
$dir .= '/' ;
}
return $dir ;
2018-04-03 12:22:20 +01:00
}
2010-04-21 08:31:18 +01:00
}