[Finder] added the component

This commit is contained in:
Fabien Potencier 2010-04-21 09:31:18 +02:00
parent c6f21e44a3
commit 72fe9fa52f
30 changed files with 1947 additions and 728 deletions

View File

@ -0,0 +1,361 @@
<?php
namespace Symfony\Components\Finder;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Finder allows to build rules to find files and directories.
*
* It is a thin wrapper around several specialized iterator classes.
*
* All rules may be invoked several times, except for ->in() method.
* Some rules are cumulative (->name() for example) whereas others are destructive
* (most recent value is used, ->maxDepth() method for example).
*
* All methods return the current Finder object to allow easy chaining:
*
* $finder = new Finder();
* $iterator = $finder->files()->name('*.php')->in(__DIR__);
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Finder
{
protected $mode = 0;
protected $names = array();
protected $notNames = array();
protected $exclude = array();
protected $filters = array();
protected $mindepth = 0;
protected $maxdepth = INF;
protected $sizes = array();
protected $followLinks = false;
protected $sort = false;
protected $ignoreVCS = true;
/**
* Restricts the matching to directories only.
*
* @return Symfony\Components\Finder The current Finder instance
*/
public function directories()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
return $this;
}
/**
* Restricts the matching to files only.
*
* @return Symfony\Components\Finder The current Finder instance
*/
public function files()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
return $this;
}
/**
* Sets the maximum directory depth.
*
* The Finder will descend at most $level levels of directories below the starting point.
*
* @param int $level The max depth
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\LimitDepthFilterIterator
*/
public function maxDepth($level)
{
$this->maxdepth = (double) $level;
return $this;
}
/**
* Sets the minimum directory depth.
*
* The Finder will start matching at level $level.
*
* @param int $level The min depth
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\LimitDepthFilterIterator
*/
public function minDepth($level)
{
$this->mindepth = (integer) $level;
return $this;
}
/**
* Adds rules that files must match.
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\FilenameFilterIterator
*/
public function name($pattern)
{
$this->names[] = $pattern;
return $this;
}
/**
* Adds rules that files must not match.
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\FilenameFilterIterator
*/
public function notName($pattern)
{
$this->notNames[] = $pattern;
return $this;
}
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
*
* @param string $size A size range string
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\SizeRangeFilterIterator
* @see Symfony\Components\Finder\NumberCompare
*/
public function size($size)
{
$this->sizes[] = new NumberCompare($size);
return $this;
}
/**
* Excludes directories.
*
* @param string $dir A directory to exclude
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\ExcludeDirectoryFilterIterator
*/
public function exclude($dir)
{
$this->exclude[] = $dir;
return $this;
}
/**
* Forces the finder to ignore version control directories.
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\IgnoreVcsFilterIterator
*/
public function ignoreVCS($ignoreVCS)
{
$this->ignoreVCS = (Boolean) $ignoreVCS;
return $this;
}
/**
* 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.
*
* @param Closure $closure An anonymous function
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\SortableIterator
*/
public function sort(\Closure $closure)
{
$this->sort = $closure;
return $this;
}
/**
* Sorts files and directories by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\SortableIterator
*/
public function sortByName()
{
$this->sort = Iterator\SortableIterator::SORT_BY_NAME;
return $this;
}
/**
* 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.
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\SortableIterator
*/
public function sortByType()
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
return $this;
}
/**
* Filters the iterator with an anonymous function.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @param Closure $closure An anonymous function
*
* @return Symfony\Components\Finder The current Finder instance
*
* @see Symfony\Components\Finder\Iterator\CustomFilterIterator
*/
public function filter(\Closure $closure)
{
$this->filters[] = $closure;
return $this;
}
/**
* Forces the following of symlinks.
*
* @return Symfony\Components\Finder The current Finder instance
*/
public function followLinks()
{
$this->followLinks = true;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
* @param string $dir A directory path
*
* @return \Iterator An iterator
*
* @throws \InvalidArgumentException if the directory does not exist
*/
public function in($dir)
{
if (is_array($dir))
{
$iterator = new Iterator\ChainIterator();
foreach ($dir as $d)
{
$iterator->attach($this->searchInDirectory($d));
}
return $iterator;
}
return $this->searchInDirectory($dir);
}
protected function searchInDirectory($dir)
{
if (!is_dir($dir))
{
throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
}
$flags = \FilesystemIterator::SKIP_DOTS;
if ($this->followLinks)
{
$flags |= \FilesystemIterator::FOLLOW_SYMLINKS;
}
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
if ($this->mode)
{
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}
if ($this->mindepth > 0 || $this->maxdepth < INF)
{
$iterator = new Iterator\LimitDepthFilterIterator($iterator, $dir, $this->mindepth, $this->maxdepth);
}
if ($this->exclude)
{
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
}
if ($this->ignoreVCS)
{
$iterator = new Iterator\IgnoreVcsFilterIterator($iterator);
}
if ($this->names || $this->notNames)
{
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}
if ($this->sizes)
{
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}
if ($this->filters)
{
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->sort)
{
$iterator = new Iterator\SortableIterator($iterator, $this->sort);
}
return $iterator;
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Symfony\Framework\WebBundle\Util;
namespace Symfony\Components\Finder;
/*
* This file is part of the Symfony framework.
@ -12,7 +12,7 @@ namespace Symfony\Framework\WebBundle\Util;
*/
/**
* Match globbing patterns against text.
* Glob matches globbing patterns against text.
*
* if match_glob("foo.*", "foo.bar") echo "matched\n";
*
@ -26,59 +26,47 @@ namespace Symfony\Framework\WebBundle\Util;
* Glob implements glob(3) style matching that can be used to match
* against text, rather than fetching names from a filesystem.
*
* based on perl Text::Glob module.
* Based on the Perl Text::Glob module.
*
* @package Symfony
* @subpackage Framework_WebBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com> php port
* @author Richard Clamp <richardc@unixbeard.net> perl version
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*/
class Glob
{
protected static $strict_leading_dot = true;
protected static $strict_wildcard_slash = true;
public static function setStrictLeadingDot($boolean)
{
self::$strict_leading_dot = $boolean;
}
public static function setStrictWildcardSlash($boolean)
{
self::$strict_wildcard_slash = $boolean;
}
/**
* Returns a compiled regex which is the equivalent of the globbing pattern.
* Returns a regexp which is the equivalent of the glob pattern.
*
* @param string $glob pattern
* @return string regex
* @param string $glob The glob pattern
*
* @return string regex The regexp
*/
public static function toRegex($glob)
static public function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true)
{
$first_byte = true;
$firstByte = true;
$escaping = false;
$in_curlies = 0;
$inCurlies = 0;
$regex = '';
$sizeGlob = strlen($glob);
for ($i = 0; $i < $sizeGlob; $i++)
{
$car = $glob[$i];
if ($first_byte)
if ($firstByte)
{
if (self::$strict_leading_dot && $car !== '.')
if ($strictLeadingDot && $car !== '.')
{
$regex .= '(?=[^\.])';
}
$first_byte = false;
$firstByte = false;
}
if ($car === '/')
{
$first_byte = true;
$firstByte = true;
}
if ($car === '.' || $car === '(' || $car === ')' || $car === '|' || $car === '+' || $car === '^' || $car === '$')
@ -87,25 +75,31 @@ class Glob
}
elseif ($car === '*')
{
$regex .= ($escaping ? '\\*' : (self::$strict_wildcard_slash ? '[^/]*' : '.*'));
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
}
elseif ($car === '?')
{
$regex .= ($escaping ? '\\?' : (self::$strict_wildcard_slash ? '[^/]' : '.'));
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
}
elseif ($car === '{')
{
$regex .= ($escaping ? '\\{' : '(');
if (!$escaping) ++$in_curlies;
$regex .= $escaping ? '\\{' : '(';
if (!$escaping)
{
++$inCurlies;
}
}
elseif ($car === '}' && $in_curlies)
elseif ($car === '}' && $inCurlies)
{
$regex .= ($escaping ? '}' : ')');
if (!$escaping) --$in_curlies;
$regex .= $escaping ? '}' : ')';
if (!$escaping)
{
--$inCurlies;
}
}
elseif ($car === ',' && $in_curlies)
elseif ($car === ',' && $inCurlies)
{
$regex .= ($escaping ? ',' : '|');
$regex .= $escaping ? ',' : '|';
}
elseif ($car === '\\')
{

View File

@ -0,0 +1,90 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* ChainIterator iterates through several iterators, one at a time.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ChainIterator implements \Iterator
{
protected $iterators;
protected $current;
protected $cursor;
/**
* Constructor.
*
* @param array $iterators An array of \Iterator instances
*/
public function __construct(array $iterators = array())
{
$this->iterators = array();
foreach ($iterators as $iterator)
{
$this->attach($iterator);
}
$this->rewind();
}
public function attach(\Iterator $iterator)
{
$this->iterators[] = $iterator;
}
public function rewind()
{
$this->cursor = 0;
$this->current = 0;
foreach ($this->iterators as $iterator)
{
$iterator->rewind();
}
}
public function valid()
{
if ($this->current > count($this->iterators) - 1)
{
return false;
}
// still something for the current iterator?
if ($this->iterators[$this->current]->valid())
{
return true;
}
// go to the next one
++$this->current;
return $this->valid();
}
public function next()
{
$this->iterators[$this->current]->next();
}
public function current()
{
return $this->iterators[$this->current]->current();
}
public function key()
{
return $this->cursor++;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* CustomFilterIterator filters files by applying anonymous functions.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class CustomFilterIterator extends \FilterIterator
{
protected $filters = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $filters An array of \Closure
*/
public function __construct(\Iterator $iterator, array $filters)
{
$this->filters = $filters;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->getInnerIterator()->current();
foreach ($this->filters as $filter)
{
if (false === $filter($fileinfo))
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* ExcludeDirectoryFilterIterator filters out directories.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ExcludeDirectoryFilterIterator extends \FilterIterator
{
protected $patterns;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $directories An array of directories to exclude
*/
public function __construct(\Iterator $iterator, array $directories)
{
$this->patterns = array();
foreach ($directories as $directory)
{
$this->patterns[] = '#/'.preg_quote($directory, '#').'(/|$)#';
}
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->getInnerIterator()->current();
foreach ($this->patterns as $pattern)
{
$path = $fileinfo->getPathname();
if ($fileinfo->isDir())
{
$path .= '/';
}
if (preg_match($pattern, $path))
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* FileTypeFilterIterator only keeps files, directories, or both.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FileTypeFilterIterator extends \FilterIterator
{
const ONLY_FILES = 1;
const ONLY_DIRECTORIES = 2;
protected $mode;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param integer $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
*/
public function __construct(\Iterator $iterator, $mode)
{
$this->mode = $mode;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->getInnerIterator()->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile())
{
return false;
}
elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir())
{
return false;
}
return true;
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace Symfony\Components\Finder\Iterator;
use Symfony\Components\Finder\Glob;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FilenameFilterIterator extends \FilterIterator
{
protected $matchRegexps;
protected $noMatchRegexps;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $matchPatterns An array of patterns that need to match
* @param array $noMatchPatterns An array of patterns that need to not match
*/
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
$this->matchRegexps = array();
foreach ($matchPatterns as $pattern)
{
$this->matchRegexps[] = $this->toRegex($pattern);
}
$this->noMatchRegexps = array();
foreach ($noMatchPatterns as $pattern)
{
$this->noMatchRegexps[] = $this->toRegex($pattern);
}
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->getInnerIterator()->current();
// should at least match one rule
if ($this->matchRegexps)
{
$match = false;
foreach ($this->matchRegexps as $regex)
{
if (preg_match($regex, $fileinfo->getFilename()))
{
$match = true;
break;
}
}
}
else
{
$match = true;
}
// should at least not match one rule to exclude
if ($this->noMatchRegexps)
{
$exclude = false;
foreach ($this->noMatchRegexps as $regex)
{
if (preg_match($regex, $fileinfo->getFilename()))
{
$exclude = true;
break;
}
}
}
else
{
$exclude = false;
}
return $match && !$exclude;
}
protected function toRegex($str)
{
if (preg_match('/^([^a-zA-Z0-9\\\\]).+?\\1[ims]?$/', $str))
{
return $str;
}
return Glob::toRegex($str);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* IgnoreVcsFilterIterator filters out VCS files and directories.
*
* It currently supports Subversion, CVS, DARCS, Gnu Arch, Monotone, Bazaar-NG, GIT, and Mercurial.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class IgnoreVcsFilterIterator extends ExcludeDirectoryFilterIterator
{
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
*/
public function __construct(\Iterator $iterator)
{
parent::__construct($iterator, array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'));
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* LimitDepthFilterIterator limits the directory depth.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class LimitDepthFilterIterator extends \FilterIterator
{
protected $minDepth = 0;
protected $maxDepth = INF;
protected $baseDir;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param string $baseDir The base directory for the depth comparison
* @param integer $minDepth The minimum depth
* @param integer $maxDepth The maximum depth
*/
public function __construct(\Iterator $iterator, $baseDir, $minDepth, $maxDepth)
{
$this->baseDir = new \SplFileInfo($baseDir);
$this->minDepth = (integer) $minDepth;
$this->maxDepth = (double) $maxDepth;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->getInnerIterator()->current();
$depth = substr_count(str_replace('\\', '/', $fileinfo->getPath()), '/') - substr_count(str_replace('\\', '/', $this->baseDir->getPathname()), '/');
if ($depth > $this->maxDepth)
{
return false;
}
if ($depth < $this->minDepth)
{
return false;
}
return true;
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* SizeRangeFilterIterator filters out files that are not in the given size range.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class SizeRangeFilterIterator extends \FilterIterator
{
protected $patterns = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $patterns An array of \NumberCompare instances
*/
public function __construct(\Iterator $iterator, array $patterns)
{
$this->patterns = $patterns;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->getInnerIterator()->current();
if (!$fileinfo->isFile())
{
return true;
}
$filesize = $fileinfo->getSize();
foreach ($this->patterns as $compare)
{
if (!$compare->test($filesize))
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Symfony\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* SortableIterator applies a sort on a given Iterator.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class SortableIterator extends \ArrayIterator
{
const SORT_BY_NAME = 1;
const SORT_BY_TYPE = 2;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param integer|\Closure $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a \Closure instance)
*/
public function __construct(\Iterator $iterator, $sort)
{
if (!$sort instanceof \Closure && self::SORT_BY_NAME == $sort)
{
$sort = function ($a, $b)
{
return strcmp($a->getRealpath(), $b->getRealpath());
};
}
elseif (!$sort instanceof \Closure && self::SORT_BY_TYPE == $sort)
{
$sort = function ($a, $b)
{
if ($a->isDir() && $b->isFile())
{
return -1;
}
elseif ($a->isFile() && $b->isDir())
{
return 1;
}
return strcmp($a->getRealpath(), $b->getRealpath());
};
}
elseif (!$sort instanceof \Closure)
{
throw new \InvalidArgumentException(sprintf('The SortableIterator takes a \Closure or a valid built-in sort algorithm as an argument (%s given).', $sort));
}
$array = new \ArrayObject(iterator_to_array($iterator));
$array->uasort($sort);
parent::__construct($array);
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace Symfony\Components\Finder;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* NumberCompare compiles a simple comparison to an anonymous
* subroutine, which you can call with a value to be tested again.
*
* Now this would be very pointless, if NumberCompare didn't understand
* magnitudes.
*
* The target value may use magnitudes of kilobytes (k, ki),
* megabytes (m, mi), or gigabytes (g, gi). Those suffixed
* with an i use the appropriate 2**n version in accordance with the
* IEC standard: http://physics.nist.gov/cuu/Units/binary.html
*
* Based on the Perl Number::Compare module.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @see http://physics.nist.gov/cuu/Units/binary.html
*/
class NumberCompare
{
protected $target;
protected $comparison;
/**
* Constructor.
*
* @param string $test A comparison string
*/
public function __construct($test)
{
if (!preg_match('#^\s*([<>=]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches))
{
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a test.', $test));
}
$this->target = $matches[2];
$this->comparison = isset($matches[1]) ? $matches[1] : '==';
$magnitude = strtolower(isset($matches[3]) ? $matches[3] : '');
switch ($magnitude)
{
case 'k':
$this->target *= 1000;
break;
case 'ki':
$this->target *= 1024;
break;
case 'm':
$this->target *= 1000000;
break;
case 'mi':
$this->target *= 1024*1024;
break;
case 'g':
$this->target *= 1000000000;
break;
case 'gi':
$this->target *= 1024*1024*1024;
break;
}
}
/**
* Tests a number against the test.
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function test($number)
{
if ($this->comparison === '>')
{
return ($number > $this->target);
}
if ($this->comparison === '>=')
{
return ($number >= $this->target);
}
if ($this->comparison === '<')
{
return ($number < $this->target);
}
if ($this->comparison === '<=')
{
return ($number <= $this->target);
}
return ($number == $this->target);
}
}

View File

@ -7,8 +7,8 @@ use Symfony\Components\Console\Input\InputOption;
use Symfony\Components\Console\Input\InputInterface;
use Symfony\Components\Console\Output\OutputInterface;
use Symfony\Components\Console\Output\Output;
use Symfony\Components\Finder\Finder;
use Symfony\Framework\WebBundle\Util\Filesystem;
use Symfony\Framework\WebBundle\Util\Finder;
use Doctrine\Common\Cli\Configuration;
use Doctrine\Common\Cli\CliController as DoctrineCliController;
use Doctrine\ORM\EntityManager;
@ -77,10 +77,14 @@ class LoadDataFixturesDoctrineCommand extends DoctrineCommand
{
if (is_dir($path))
{
$found = Finder::type('file')
$finder = new Finder();
$found = iterator_to_array($finder
->files()
->name('*.php')
->in($path);
} else {
->in($path));
}
else
{
$found = array($path);
}
$files = array_merge($files, $found);

View File

@ -1,597 +0,0 @@
<?php
namespace Symfony\Framework\WebBundle\Util;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Allows to build rules to find files and directories.
*
* All rules may be invoked several times, except for ->in() method.
* Some rules are cumulative (->name() for example) whereas others are destructive
* (most recent value is used, ->maxdepth() method for example).
*
* All methods return the current Finder object to allow easy chaining:
*
* $files = Finder::type('file')->name('*.php')->in(.);
*
* Interface loosely based on perl File::Find::Rule module.
*
* @package Symfony
* @subpackage Framework_WebBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Finder
{
protected $type = 'file';
protected $names = array();
protected $prunes = array();
protected $discards = array();
protected $execs = array();
protected $mindepth = 0;
protected $sizes = array();
protected $maxdepth = 1000000;
protected $relative = false;
protected $followLinks = false;
protected $sort = false;
protected $ignoreVersionControl = true;
/**
* Sets maximum directory depth.
*
* Finder will descend at most $level levels of directories below the starting point.
*
* @param int $level
* @return object current Finder object
*/
public function maxDepth($level)
{
$this->maxdepth = $level;
return $this;
}
/**
* Sets minimum directory depth.
*
* Finder will start applying tests at level $level.
*
* @param int $level
* @return object current Finder object
*/
public function minDepth($level)
{
$this->mindepth = $level;
return $this;
}
public function getType()
{
return $this->type;
}
/**
* Sets the type of elements to returns.
*
* @param string $name directory or file or any (for both file and directory)
* @return object new Finder object
*/
public static function type($name)
{
$finder = new self();
return $finder->setType($name);
}
/**
* Sets the type of elements to returns.
*
* @param string $name directory or file or any (for both file and directory)
* @return Finder Current object
*/
public function setType($name)
{
$name = strtolower($name);
if (substr($name, 0, 3) === 'dir')
{
$this->type = 'directory';
return $this;
}
if ($name === 'any')
{
$this->type = 'any';
return $this;
}
$this->type = 'file';
return $this;
}
/*
* glob, patterns (must be //) or strings
*/
protected function toRegex($str)
{
if (preg_match('/^(!)?([^a-zA-Z0-9\\\\]).+?\\2[ims]?$/', $str))
{
return $str;
}
return Glob::toRegex($str);
}
protected function argsToArray($arg_list, $not = false)
{
$list = array();
$nbArgList = count($arg_list);
for ($i = 0; $i < $nbArgList; $i++)
{
if (is_array($arg_list[$i]))
{
foreach ($arg_list[$i] as $arg)
{
$list[] = array($not, $this->toRegex($arg));
}
}
else
{
$list[] = array($not, $this->toRegex($arg_list[$i]));
}
}
return $list;
}
/**
* Adds rules that files must match.
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
*
* @param list a list of patterns, globs or strings
* @return Finder Current object
*/
public function name()
{
$args = func_get_args();
$this->names = array_merge($this->names, $this->argsToArray($args));
return $this;
}
/**
* Adds rules that files must not match.
*
* @see ->name()
* @param list a list of patterns, globs or strings
* @return Finder Current object
*/
public function notName()
{
$args = func_get_args();
$this->names = array_merge($this->names, $this->argsToArray($args, true));
return $this;
}
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
*
* @param list a list of comparison strings
* @return Finder Current object
*/
public function size()
{
$args = func_get_args();
$numargs = count($args);
for ($i = 0; $i < $numargs; $i++)
{
$this->sizes[] = new NumberCompare($args[$i]);
}
return $this;
}
/**
* Traverses no further.
*
* @param list a list of patterns, globs to match
* @return Finder Current object
*/
public function prune()
{
$args = func_get_args();
$this->prunes = array_merge($this->prunes, $this->argsToArray($args));
return $this;
}
/**
* Discards elements that matches.
*
* @param list a list of patterns, globs to match
* @return Finder Current object
*/
public function discard()
{
$args = func_get_args();
$this->discards = array_merge($this->discards, $this->argsToArray($args));
return $this;
}
/**
* Ignores version control directories.
*
* Currently supports Subversion, CVS, DARCS, Gnu Arch, Monotone, Bazaar-NG, GIT, Mercurial
*
* @param bool $ignore false when version control directories shall be included (default is true)
*
* @return Finder Current object
*/
public function ignoreVersionControl($ignore = true)
{
$this->ignoreVersionControl = $ignore;
return $this;
}
/**
* Returns files and directories ordered by name
*
* @return Finder Current object
*/
public function sortByName()
{
$this->sort = 'name';
return $this;
}
/**
* Returns files and directories ordered by type (directories before files), then by name
*
* @return Finder Current object
*/
public function sortByType()
{
$this->sort = 'type';
return $this;
}
/**
* Executes function or method for each element.
*
* Element match if function or method returns true.
*
* $finder->exec('myfunction');
* $finder->exec(array($object, 'mymethod'));
*
* @param mixed function or method to call
* @return Finder Current object
*
* @throws \InvalidArgumentException If function or method does not exist
*/
public function exec()
{
$args = func_get_args();
$numargs = count($args);
for ($i = 0; $i < $numargs; $i++)
{
if (is_array($args[$i]) && !method_exists($args[$i][0], $args[$i][1]))
{
throw new \InvalidArgumentException(sprintf('Method "%s" does not exist for object "%s".', $args[$i][1], $args[$i][0]));
}
if (!is_array($args[$i]) && !function_exists($args[$i]))
{
throw new \InvalidArgumentException(sprintf('Function "%s" does not exist.', $args[$i]));
}
$this->execs[] = $args[$i];
}
return $this;
}
/**
* Returns relative paths for all files and directories.
*
* @return Finder Current object
*/
public function relative()
{
$this->relative = true;
return $this;
}
/**
* Symlink following.
*
* @return Finder Current object
*/
public function followLinks()
{
$this->followLinks = true;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
* @return array list of files and directories
*/
public function in()
{
$files = array();
$here_dir = getcwd();
$finder = clone $this;
if ($this->ignoreVersionControl)
{
$ignores = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
$finder->discard($ignores)->prune($ignores);
}
// first argument is an array?
$numargs = func_num_args();
$arg_list = func_get_args();
if ($numargs === 1 && is_array($arg_list[0]))
{
$arg_list = $arg_list[0];
$numargs = count($arg_list);
}
for ($i = 0; $i < $numargs; $i++)
{
$dir = realpath($arg_list[$i]);
if (!is_dir($dir))
{
continue;
}
$dir = str_replace('\\', '/', $dir);
// absolute path?
if (!self::isPathAbsolute($dir))
{
$dir = $here_dir.'/'.$dir;
}
$new_files = str_replace('\\', '/', $finder->searchIn($dir));
if ($this->relative)
{
$new_files = str_replace(rtrim($dir, '/').'/', '', $new_files);
}
$files = array_merge($files, $new_files);
}
if ($this->sort === 'name')
{
sort($files);
}
return array_unique($files);
}
protected function searchIn($dir, $depth = 0)
{
if ($depth > $this->maxdepth)
{
return array();
}
$dir = realpath($dir);
if ((!$this->followLinks) && is_link($dir))
{
return array();
}
$files = array();
$temp_files = array();
$temp_folders = array();
if (is_dir($dir))
{
$current_dir = opendir($dir);
while (false !== $entryname = readdir($current_dir))
{
if ($entryname == '.' || $entryname == '..') continue;
$current_entry = $dir.DIRECTORY_SEPARATOR.$entryname;
if ((!$this->followLinks) && is_link($current_entry))
{
continue;
}
if (is_dir($current_entry))
{
if ($this->sort === 'type')
{
$temp_folders[$entryname] = $current_entry;
}
else
{
if (($this->type === 'directory' || $this->type === 'any') && ($depth >= $this->mindepth) && !$this->isDiscarded($dir, $entryname) && $this->matchNames($dir, $entryname) && $this->execOk($dir, $entryname))
{
$files[] = $current_entry;
}
if (!$this->isPruned($dir, $entryname))
{
$files = array_merge($files, $this->searchIn($current_entry, $depth + 1));
}
}
}
else
{
if (($this->type !== 'directory' || $this->type === 'any') && ($depth >= $this->mindepth) && !$this->isDiscarded($dir, $entryname) && $this->matchNames($dir, $entryname) && $this->sizeOk($dir, $entryname) && $this->execOk($dir, $entryname))
{
if ($this->sort === 'type')
{
$temp_files[] = $current_entry;
}
else
{
$files[] = $current_entry;
}
}
}
}
if ($this->sort === 'type')
{
ksort($temp_folders);
foreach($temp_folders as $entryname => $current_entry)
{
if (($this->type === 'directory' || $this->type === 'any') && ($depth >= $this->mindepth) && !$this->isDiscarded($dir, $entryname) && $this->matchNames($dir, $entryname) && $this->execOk($dir, $entryname))
{
$files[] = $current_entry;
}
if (!$this->isPruned($dir, $entryname))
{
$files = array_merge($files, $this->searchIn($current_entry, $depth + 1));
}
}
sort($temp_files);
$files = array_merge($files, $temp_files);
}
closedir($current_dir);
}
return $files;
}
protected function matchNames($dir, $entry)
{
if (!count($this->names)) return true;
// Flags indicating that there was attempts to match
// at least one "not_name" or "name" rule respectively
// to following variables:
$one_not_name_rule = false;
$one_name_rule = false;
foreach ($this->names as $args)
{
list($not, $regex) = $args;
$not ? $one_not_name_rule = true : $one_name_rule = true;
if (preg_match($regex, $entry))
{
// We must match ONLY ONE "not_name" or "name" rule:
// if "not_name" rule matched then we return "false"
// if "name" rule matched then we return "true"
return $not ? false : true;
}
}
if ($one_not_name_rule && $one_name_rule)
{
return false;
}
else if ($one_not_name_rule)
{
return true;
}
else if ($one_name_rule)
{
return false;
}
return true;
}
protected function sizeOk($dir, $entry)
{
if (0 === count($this->sizes)) return true;
if (!is_file($dir.DIRECTORY_SEPARATOR.$entry)) return true;
$filesize = filesize($dir.DIRECTORY_SEPARATOR.$entry);
foreach ($this->sizes as $number_compare)
{
if (!$number_compare->test($filesize)) return false;
}
return true;
}
protected function isPruned($dir, $entry)
{
if (0 === count($this->prunes)) return false;
foreach ($this->prunes as $args)
{
$regex = $args[1];
if (preg_match($regex, $entry)) return true;
}
return false;
}
protected function isDiscarded($dir, $entry)
{
if (0 === count($this->discards)) return false;
foreach ($this->discards as $args)
{
$regex = $args[1];
if (preg_match($regex, $entry)) return true;
}
return false;
}
protected function execOk($dir, $entry)
{
if (0 === count($this->execs)) return true;
foreach ($this->execs as $exec)
{
if (!call_user_func_array($exec, array($dir, $entry))) return false;
}
return true;
}
public static function isPathAbsolute($path)
{
if ($path{0} === '/' || $path{0} === '\\' ||
(strlen($path) > 3 && ctype_alpha($path{0}) &&
$path{1} === ':' &&
($path{2} === '\\' || $path{2} === '/')
)
)
{
return true;
}
return false;
}
}

View File

@ -1,89 +0,0 @@
<?php
namespace Symfony\Framework\WebBundle\Util;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Numeric comparisons.
*
* NumberCompare compiles a simple comparison to an anonymous
* subroutine, which you can call with a value to be tested again.
* Now this would be very pointless, if NumberCompare didn't understand
* magnitudes.
* The target value may use magnitudes of kilobytes (k, ki),
* megabytes (m, mi), or gigabytes (g, gi). Those suffixed
* with an i use the appropriate 2**n version in accordance with the
* IEC standard: http://physics.nist.gov/cuu/Units/binary.html
*
* based on perl Number::Compare module.
*
* @package Symfony
* @subpackage Framework_WebBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com> php port
* @author Richard Clamp <richardc@unixbeard.net> perl version
* @copyright 2004-2005 Fabien Potencier <fabien.potencier@symfony-project.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @see http://physics.nist.gov/cuu/Units/binary.html
*/
class NumberCompare
{
protected $test = '';
public function __construct($test)
{
$this->test = $test;
}
/**
* @throws \RuntimeException If the test is not understood
*/
public function test($number)
{
if (!preg_match('{^([<>]=?)?(.*?)([kmg]i?)?$}i', $this->test, $matches))
{
throw new \RuntimeException(sprintf('Don\'t understand "%s" as a test.', $this->test));
}
$target = array_key_exists(2, $matches) ? $matches[2] : '';
$magnitude = array_key_exists(3, $matches) ? $matches[3] : '';
if (strtolower($magnitude) === 'k') $target *= 1000;
if (strtolower($magnitude) === 'ki') $target *= 1024;
if (strtolower($magnitude) === 'm') $target *= 1000000;
if (strtolower($magnitude) === 'mi') $target *= 1024*1024;
if (strtolower($magnitude) === 'g') $target *= 1000000000;
if (strtolower($magnitude) === 'gi') $target *= 1024*1024*1024;
$comparison = array_key_exists(1, $matches) ? $matches[1] : '==';
if ($comparison === '==' || $comparison == '')
{
return ($number == $target);
}
if ($comparison === '>')
{
return ($number > $target);
}
if ($comparison === '>=')
{
return ($number >= $target);
}
if ($comparison === '<')
{
return ($number < $target);
}
if ($comparison === '<=')
{
return ($number <= $target);
}
return false;
}
}

View File

@ -0,0 +1,202 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder;
use Symfony\Components\Finder\Finder;
require_once __DIR__.'/Iterator/RealIteratorTestCase.php';
class FinderTest extends Iterator\RealIteratorTestCase
{
static protected $tmpDir;
static public function setUpBeforeClass()
{
parent::setUpBeforeClass();
self::$tmpDir = sys_get_temp_dir().'/symfony2_finder/';
}
public function testDirectories()
{
$finder = new Finder();
$this->assertSame($finder, $finder->directories());
$this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir));
$finder = new Finder();
$finder->directories();
$finder->files();
$finder->directories();
$this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir));
}
public function testFiles()
{
$finder = new Finder();
$this->assertSame($finder, $finder->files());
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir));
$finder = new Finder();
$finder->files();
$finder->directories();
$finder->files();
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir));
}
public function testMaxDepth()
{
$finder = new Finder();
$this->assertSame($finder, $finder->maxDepth(0));
$this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
}
public function testMinDepth()
{
$finder = new Finder();
$this->assertSame($finder, $finder->minDepth(1));
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp')), $finder->in(self::$tmpDir));
}
public function testMinMaxDepth()
{
$finder = new Finder();
$finder->maxDepth(0);
$finder->minDepth(1);
$this->assertIterator(array(), $finder->in(self::$tmpDir));
}
public function testName()
{
$finder = new Finder();
$this->assertSame($finder, $finder->name('*.php'));
$this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir));
$finder = new Finder();
$finder->name('test.ph*');
$finder->name('test.py');
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir));
}
public function testNotName()
{
$finder = new Finder();
$this->assertSame($finder, $finder->notName('*.php'));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.py', 'toto')), $finder->in(self::$tmpDir));
$finder = new Finder();
$finder->notName('*.php');
$finder->notName('*.py');
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->in(self::$tmpDir));
$finder = new Finder();
$finder->name('test.ph*');
$finder->name('test.py');
$finder->notName('*.php');
$finder->notName('*.py');
$this->assertIterator(array(), $finder->in(self::$tmpDir));
}
public function testSize()
{
$finder = new Finder();
$this->assertSame($finder, $finder->files()->size('< 1K')->size('> 500'));
$this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir));
}
public function testExclude()
{
$finder = new Finder();
$this->assertSame($finder, $finder->exclude('foo'));
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
}
public function testIgnoreVCS()
{
$finder = new Finder();
$this->assertSame($finder, $finder->ignoreVCS(false));
$this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
$finder = new Finder();
$this->assertSame($finder, $finder->ignoreVCS(true));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
}
public function testSortByName()
{
$finder = new Finder();
$this->assertSame($finder, $finder->sortByName());
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
}
public function testSortByType()
{
$finder = new Finder();
$this->assertSame($finder, $finder->sortByType());
$this->assertIterator($this->toAbsolute(array('foo', 'toto', 'foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir));
}
public function testSort()
{
$finder = new Finder();
$this->assertSame($finder, $finder->sort(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealpath(), $b->getRealpath()); }));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
}
public function testFilter()
{
$finder = new Finder();
$this->assertSame($finder, $finder->filter(function (\SplFileInfo $f) { return preg_match('/test/', $f) > 0; }));
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir));
}
public function testFollowLinks()
{
if ('\\' == DIRECTORY_SEPARATOR)
{
return;
}
$finder = new Finder();
$this->assertSame($finder, $finder->followLinks());
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir));
}
public function testIn()
{
$finder = new Finder();
try
{
$finder->in('foobar');
$this->fail('->in() throws a \InvalidArgumentException if the directory does not exist');
}
catch (\Exception $e)
{
$this->assertInstanceOf('InvalidArgumentException', $e, '->in() throws a \InvalidArgumentException if the directory does not exist');
}
$finder = new Finder();
$iterator = $finder->files()->name('*.php')->maxDepth(0)->in(array(self::$tmpDir, __DIR__));
$this->assertIterator(array(self::$tmpDir.'test.php', __DIR__.'/FinderTest.php', __DIR__.'/GlobTest.php', __DIR__.'/NumberCompareTest.php'), $iterator);
}
protected function toAbsolute($files)
{
$f = array();
foreach ($files as $file)
{
$f[] = self::$tmpDir.$file;
}
return $f;
}
}

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder;
use Symfony\Components\Finder\Glob;
class GlobTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getToRegexData
*/
public function testToRegex($glob, $match, $noMatch)
{
foreach ($match as $m)
{
$this->assertRegExp(Glob::toRegex($glob), $m, '::toRegex() converts a glob to a regexp');
}
foreach ($noMatch as $m)
{
$this->assertNotRegExp(Glob::toRegex($glob), $m, '::toRegex() converts a glob to a regexp');
}
}
public function getToRegexData()
{
return array(
array('', array(''), array('f', '/')),
array('*', array('foo'), array('foo/', '/foo')),
array('foo.*', array('foo.php', 'foo.a', 'foo.'), array('fooo.php', 'foo.php/foo')),
array('fo?', array('foo', 'fot'), array('fooo', 'ffoo', 'fo/')),
array('fo{o,t}', array('foo', 'fot'), array('fob', 'fo/')),
array('foo(bar|foo)', array('foo(bar|foo)'), array('foobar', 'foofoo')),
array('foo,bar', array('foo,bar'), array('foo', 'bar')),
array('fo{o,\\,}', array('foo', 'fo,'), array()),
array('fo{o,\\\\}', array('foo', 'fo\\'), array()),
array('/foo', array('/foo'), array('foo')),
);
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\ChainIterator;
require_once __DIR__.'/IteratorTestCase.php';
class ChainIteratorTest extends IteratorTestCase
{
public function testAccept()
{
$inner1 = new Iterator(array('test.php', 'test.py'));
$inner2 = new Iterator(array());
$inner3 = new Iterator(array('foo.php'));
$iterator = new ChainIterator(array($inner1, $inner2, $inner3));
$this->assertIterator(array('test.php', 'test.py', 'foo.php'), $iterator);
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\CustomFilterIterator;
require_once __DIR__.'/IteratorTestCase.php';
class CustomFilterIteratorTest extends IteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($filters, $expected)
{
$inner = new Iterator(array('test.php', 'test.py', 'foo.php'));
$iterator = new CustomFilterIterator($inner, $filters);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(array(function (\SplFileInfo $fileinfo) { return false; }), array()),
array(array(function (\SplFileInfo $fileinfo) { return preg_match('/^test/', $fileinfo) > 0; }), array('test.php', 'test.py')),
);
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\ExcludeDirectoryFilterIterator;
require_once __DIR__.'/IteratorTestCase.php';
class ExcludeDirectoryFilterIteratorTest extends IteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($directories, $expected)
{
$inner = new Iterator(array('/foo/test.php', '/foo/test.py', '/bar/foo.php'));
$iterator = new ExcludeDirectoryFilterIterator($inner, $directories);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(array('foo'), array('/bar/foo.php')),
array(array('fo'), array('/foo/test.php', '/foo/test.py', '/bar/foo.php')),
);
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\FileTypeFilterIterator;
require_once __DIR__.'/RealIteratorTestCase.php';
class FileTypeFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($mode, $expected)
{
$inner = new Iterator(self::$files);
$iterator = new FileTypeFilterIterator($inner, $mode);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(FileTypeFilterIterator::ONLY_FILES, array(sys_get_temp_dir().'/symfony2_finder/test.py', sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp', sys_get_temp_dir().'/symfony2_finder/test.php')),
array(FileTypeFilterIterator::ONLY_DIRECTORIES, array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/toto')),
);
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\FilenameFilterIterator;
require_once __DIR__.'/IteratorTestCase.php';
class FilenameFilterIteratorTest extends IteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($matchPatterns, $noMatchPatterns, $expected)
{
$inner = new Iterator(array('test.php', 'test.py', 'foo.php'));
$iterator = new FilenameFilterIterator($inner, $matchPatterns, $noMatchPatterns);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(array('test.*'), array(), array('test.php', 'test.py')),
array(array(), array('test.*'), array('foo.php')),
array(array('*.php'), array('test.*'), array('foo.php')),
array(array('*.php', '*.py'), array('foo.*'), array('test.php', 'test.py')),
array(array('/\.php$/'), array(), array('test.php', 'foo.php')),
array(array(), array('/\.php$/'), array('test.py')),
);
}
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\IgnoreVcsFilterIterator;
require_once __DIR__.'/IteratorTestCase.php';
class IgnoreVcsFilterIteratorTest extends IteratorTestCase
{
public function testAccept()
{
$inner = new Iterator(array('/.git/test.php', '/foo/test.py', '/bar/foo.php'));
$iterator = new IgnoreVcsFilterIterator($inner);
$this->assertIterator(array('/foo/test.py', '/bar/foo.php'), $iterator);
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Symfony\Tests\Components\Finder\Iterator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
class Iterator implements \Iterator
{
protected $values;
public function __construct(array $values = array())
{
$this->values = array();
foreach ($values as $value)
{
$this->attach(new \SplFileInfo($value));
}
$this->rewind();
}
public function attach(\SplFileInfo $fileinfo)
{
$this->values[] = $fileinfo;
}
public function rewind()
{
reset($this->values);
}
public function valid()
{
return false !== $this->current();
}
public function next()
{
next($this->values);
}
public function current()
{
return current($this->values);
}
public function key()
{
return key($this->values);
}
}

View File

@ -0,0 +1,24 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
require_once __DIR__.'/Iterator.php';
class IteratorTestCase extends \PHPUnit_Framework_TestCase
{
protected function assertIterator($expected, \Iterator $iterator)
{
$values = array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator));
$this->assertEquals($expected, array_values($values));
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\LimitDepthFilterIterator;
require_once __DIR__.'/RealIteratorTestCase.php';
class LimitDepthFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($baseDir, $minDepth, $maxDepth, $expected)
{
$inner = new Iterator(self::$files);
$iterator = new LimitDepthFilterIterator($inner, $baseDir, $minDepth, $maxDepth);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(sys_get_temp_dir().'/symfony2_finder', 0, INF, array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/test.py', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp', sys_get_temp_dir().'/symfony2_finder/test.php', sys_get_temp_dir().'/symfony2_finder/toto')),
array(sys_get_temp_dir().'/symfony2_finder', 0, 0, array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/test.py', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/test.php', sys_get_temp_dir().'/symfony2_finder/toto')),
array(sys_get_temp_dir().'/symfony2_finder', 1, 1, array(sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp')),
);
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
require_once __DIR__.'/IteratorTestCase.php';
class RealIteratorTestCase extends IteratorTestCase
{
static protected $files;
static public function setUpBeforeClass()
{
$tmpDir = sys_get_temp_dir().'/symfony2_finder';
self::$files = array($tmpDir.'/.git', $tmpDir.'/test.py', $tmpDir.'/foo', $tmpDir.'/foo/bar.tmp', $tmpDir.'/test.php', $tmpDir.'/toto');
if (is_dir($tmpDir))
{
self::tearDownAfterClass();
rmdir($tmpDir);
}
mkdir($tmpDir);
foreach (self::$files as $file)
{
if (false !== ($pos = strpos($file, '.')) && '/' !== $file[$pos - 1])
{
touch($file);
}
else
{
mkdir($file);
}
}
file_put_contents($tmpDir.'/test.php', str_repeat(' ', 800));
file_put_contents($tmpDir.'/test.py', str_repeat(' ', 2000));
}
static public function tearDownAfterClass()
{
foreach (self::$files as $file)
{
if (false !== ($pos = strpos($file, '.')) && '/' !== $file[$pos - 1])
{
@unlink($file);
}
else
{
@rmdir($file);
}
}
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Components\Finder\NumberCompare;
require_once __DIR__.'/RealIteratorTestCase.php';
class SizeRangeFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($size, $expected)
{
$inner = new Iterator(self::$files);
$iterator = new SizeRangeFilterIterator($inner, $size);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(array(new NumberCompare('< 1K'), new NumberCompare('> 0.5K')), array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/test.php', sys_get_temp_dir().'/symfony2_finder/toto')),
);
}
}

View File

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\SortableIterator;
require_once __DIR__.'/RealIteratorTestCase.php';
class SortableIteratorTest extends RealIteratorTestCase
{
public function testConstructor()
{
try
{
new SortableIterator(new Iterator(array()), 'foobar');
$this->fail('__construct() throws an \InvalidArgumentException exception if the mode is not valid');
}
catch (\Exception $e)
{
$this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException exception if the mode is not valid');
}
}
/**
* @dataProvider getAcceptData
*/
public function testAccept($mode, $expected)
{
$inner = new Iterator(self::$files);
$iterator = new SortableIterator($inner, $mode);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(SortableIterator::SORT_BY_NAME, array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp', sys_get_temp_dir().'/symfony2_finder/test.php', sys_get_temp_dir().'/symfony2_finder/test.py', sys_get_temp_dir().'/symfony2_finder/toto')),
array(SortableIterator::SORT_BY_TYPE, array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/toto', sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp', sys_get_temp_dir().'/symfony2_finder/test.php', sys_get_temp_dir().'/symfony2_finder/test.py')),
array(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealpath(), $b->getRealpath()); }, array(sys_get_temp_dir().'/symfony2_finder/.git', sys_get_temp_dir().'/symfony2_finder/foo', sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp', sys_get_temp_dir().'/symfony2_finder/test.php', sys_get_temp_dir().'/symfony2_finder/test.py', sys_get_temp_dir().'/symfony2_finder/toto')),
);
}
}

View File

@ -0,0 +1,76 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder;
use Symfony\Components\Finder\NumberCompare;
class NumberCompareTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
try
{
new NumberCompare('foobar');
$this->fail('->test() throws an \InvalidArgumentException if the test expression is not valid.');
}
catch (\Exception $e)
{
$this->assertInstanceOf('InvalidArgumentException', $e, '->test() throws an \InvalidArgumentException if the test expression is not valid.');
}
}
/**
* @dataProvider getTestData
*/
public function testTest($test, $match, $noMatch)
{
foreach ($match as $m)
{
$c = new NumberCompare($test);
$this->assertTrue($c->test($m), '->test() tests a string against the expression');
}
foreach ($noMatch as $m)
{
$c = new NumberCompare($test);
$this->assertFalse($c->test($m), '->test() tests a string against the expression');
}
}
public function getTestData()
{
return array(
array('< 1000', array('500', '999'), array('1000', '1500')),
array('< 1K', array('500', '999'), array('1000', '1500')),
array('<1k', array('500', '999'), array('1000', '1500')),
array(' < 1 K ', array('500', '999'), array('1000', '1500')),
array('<= 1K', array('1000'), array('1001')),
array('> 1K', array('1001'), array('1000')),
array('>= 1K', array('1000'), array('999')),
array('< 1KI', array('500', '1023'), array('1024', '1500')),
array('<= 1KI', array('1024'), array('1025')),
array('> 1KI', array('1025'), array('1024')),
array('>= 1KI', array('1024'), array('1023')),
array('1KI', array('1024'), array('1023', '1025')),
array('==1KI', array('1024'), array('1023', '1025')),
array('==1m', array('1000000'), array('999999', '1000001')),
array('==1mi', array(1024*1024), array(1024*1024-1, 1024*1024+1)),
array('==1g', array('1000000000'), array('999999999', '1000000001')),
array('==1gi', array(1024*1024*1024), array(1024*1024*1024-1, 1024*1024*1024+1)),
);
}
}