merged branch gajdaw/component_finder_path_notPath (PR #4739)
This PR was merged into the master branch. Commits -------4e21bf2
[Finder] Added path & notPath support to gnu find adapter.6258d12
[Finder] Fixed expression classes.5c6dbeb
[Finder] Fixed tests.c36dfc1
[Component][Finder] ->path(), ->notPath() methods (with basic tests) Discussion ---------- [2.2][Finder] ->pathContains(), ->pathNotContains() methods (with basic tests) Bug fix: no Feature addition: yes Backwards compatibility break: no Symfony2 tests pass: [![Build Status](https://secure.travis-ci.org/gajdaw/symfony.png?branch=component_finder_path_notPath)](http://travis-ci.org/gajdaw/symfony) Fixes the following tickets: #4581 Todo: - License of the code: MIT Documentation PR: - Two additional methods: `Finder->path()` and `Finder->notPath()`. They allow filtering with paths, e.g. `->path('some/special/dir')`. --------------------------------------------------------------------------- by fabpot at 2012-09-21T05:52:01Z Can you submit a PR on symfony/symfony-docs to update the documentation and reference it here before I merge? Thanks. --------------------------------------------------------------------------- by gajdaw at 2012-09-21T10:54:50Z I've already done it in PR1527. https://github.com/symfony/symfony-docs/pull/1527 --------------------------------------------------------------------------- by stof at 2012-09-21T22:15:46Z Actually, to be BC in PHPUnit when switching to Finder (this feature is the reason why the switch was reverted in 3.7), they will need the support of the glob syntax. Should it be supported directly in the Finder or should we consider that PHPUnit will have to put the code converting globs to regexes themselves before calling the finder ? --------------------------------------------------------------------------- by fabpot at 2012-09-23T13:15:08Z @stof what about supporting globs/patterns for the `in()` method? --------------------------------------------------------------------------- by stof at 2012-09-23T13:38:00Z yeah, this could be a good idea too --------------------------------------------------------------------------- by gajdaw at 2012-09-26T06:11:56Z Supporting globs in `path(), notPath()` methods is trivial: we have `Glob::toRegex()`. The only thing to do is to convert (when necessary) parameter sent to `path(), notPath()`. --------------------------------------------------------------------------- by stof at 2012-10-13T17:19:08Z @gajdaw can you update this PR with the glob support and rebase it ? --------------------------------------------------------------------------- by fabpot at 2012-10-29T11:20:55Z @gajdaw You also need to rebase and update the new adapters accordingly. Maybe @jfsimon can help. --------------------------------------------------------------------------- by jfsimon at 2012-10-29T16:22:25Z With pleasure! @gajdaw let me know if I can do anything. --------------------------------------------------------------------------- by gajdaw at 2012-10-29T16:48:10Z I have moved `->path()` and `->notPath()` methods to `PhpAdapter`. This implementation passes all the tests on Windows, but Travis reports failures. I think that similar methods should be implemented for `GnuFindAdapter`. @jfsimon What do you think? --------------------------------------------------------------------------- by jfsimon at 2012-10-29T17:03:36Z @gajdaw Travis says class 'Symfony\Component\HttpKernel\Exception\InternalServerErrorHttpException' not found in /home/travis/builds/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Exception/FlattenExceptionTest.php on line 83. This is weird. And yes, `AdapterInterface` and `GnuFindAdapter` should be updated too. I can work on it if you like. --------------------------------------------------------------------------- by fabpot at 2012-10-29T17:07:46Z I've just fixed the unit tests --------------------------------------------------------------------------- by gajdaw at 2012-10-29T17:25:43Z @jfsimon Can implement `path(), notPath()` for `GnuFindAdapter`? I have no time to analyse Gnu's `find` command at the moment. Thanks! --------------------------------------------------------------------------- by jfsimon at 2012-10-29T17:30:13Z @gajdaw okay. --------------------------------------------------------------------------- by jfsimon at 2012-10-29T19:05:26Z @gajdaw work complete! I cant make a PR on your repos :-/ Could you merge my repos https://github.com/jfsimon/symfony/tree/component_finder_path_notPath please? Or maybe could I post a new PR on symfony/master. --------------------------------------------------------------------------- by gajdaw at 2012-10-30T05:34:17Z @jfsimon I have pulled your changes. I don't know wheather you should post a new PR.
This commit is contained in:
commit
d574e239d5
@ -31,6 +31,8 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
protected $dates = array();
|
||||
protected $filters = array();
|
||||
protected $sort = false;
|
||||
protected $paths = array();
|
||||
protected $notPaths = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -40,7 +42,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->followLinks = $followLinks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -50,7 +52,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->mode = $mode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -80,7 +82,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -90,7 +92,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->exclude = $exclude;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -100,7 +102,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->names = $names;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -110,7 +112,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->notNames = $notNames;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -120,7 +122,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->contains = $contains;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -130,7 +132,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->notContains = $notContains;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -140,7 +142,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->sizes = $sizes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -150,7 +152,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->dates = $dates;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -160,7 +162,7 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->filters = $filters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -170,5 +172,25 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||
$this->sort = $sort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPath(array $paths)
|
||||
{
|
||||
$this->paths = $paths;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setNotPath(array $notPaths)
|
||||
{
|
||||
$this->notPaths = $notPaths;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -21,103 +21,117 @@ interface AdapterInterface
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setFollowLinks($followLinks);
|
||||
public function setFollowLinks($followLinks);
|
||||
|
||||
/**
|
||||
* @param int $mode
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setMode($mode);
|
||||
public function setMode($mode);
|
||||
|
||||
/**
|
||||
* @param array $exclude
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setExclude(array $exclude);
|
||||
public function setExclude(array $exclude);
|
||||
|
||||
/**
|
||||
* @param array $depths
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setDepths(array $depths);
|
||||
public function setDepths(array $depths);
|
||||
|
||||
/**
|
||||
* @param array $names
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setNames(array $names);
|
||||
public function setNames(array $names);
|
||||
|
||||
/**
|
||||
* @param array $notNames
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setNotNames(array $notNames);
|
||||
public function setNotNames(array $notNames);
|
||||
|
||||
/**
|
||||
* @param array $contains
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setContains(array $contains);
|
||||
public function setContains(array $contains);
|
||||
|
||||
/**
|
||||
* @param array $notContains
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setNotContains(array $notContains);
|
||||
public function setNotContains(array $notContains);
|
||||
|
||||
/**
|
||||
* @param array $sizes
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setSizes(array $sizes);
|
||||
public function setSizes(array $sizes);
|
||||
|
||||
/**
|
||||
* @param array $dates
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setDates(array $dates);
|
||||
public function setDates(array $dates);
|
||||
|
||||
/**
|
||||
* @param array $filters
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setFilters(array $filters);
|
||||
public function setFilters(array $filters);
|
||||
|
||||
/**
|
||||
* @param \Closure|int $sort
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
function setSort($sort);
|
||||
public function setSort($sort);
|
||||
|
||||
/**
|
||||
* @param array $path
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
public function setPath(array $paths);
|
||||
|
||||
/**
|
||||
* @param array $notPaths
|
||||
*
|
||||
* @return AdapterInterface Current instance
|
||||
*/
|
||||
public function setNotPath(array $notPaths);
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
*
|
||||
* @return \Iterator Result iterator
|
||||
*/
|
||||
function searchInDirectory($dir);
|
||||
public function searchInDirectory($dir);
|
||||
|
||||
/**
|
||||
* Tests adapter support for current platform.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function isSupported();
|
||||
public function isSupported();
|
||||
|
||||
/**
|
||||
* Returns adapter name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getName();
|
||||
public function getName();
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ class GnuFindAdapter extends AbstractAdapter
|
||||
|
||||
$this->buildNamesFiltering($find, $this->names);
|
||||
$this->buildNamesFiltering($find, $this->notNames, true);
|
||||
$this->buildPathsFiltering($find, $dir, $this->paths);
|
||||
$this->buildPathsFiltering($find, $dir, $this->notPaths, true);
|
||||
$this->buildSizesFiltering($find, $this->sizes);
|
||||
$this->buildDatesFiltering($find, $this->dates);
|
||||
|
||||
@ -151,7 +153,7 @@ class GnuFindAdapter extends AbstractAdapter
|
||||
foreach ($names as $i => $name) {
|
||||
$expr = Expression::create($name);
|
||||
|
||||
// Fixes 'not search' and 'fuls path matching' regex problems.
|
||||
// Fixes 'not search' and 'full path matching' regex problems.
|
||||
// - Jokers '.' are replaced by [^/].
|
||||
// - We add '[^/]*' before and after regex (if no ^|$ flags are present).
|
||||
if ($expr->isRegex()) {
|
||||
@ -177,6 +179,44 @@ class GnuFindAdapter extends AbstractAdapter
|
||||
$command->cmd(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Command $command
|
||||
* @param string $dir
|
||||
* @param string[] $paths
|
||||
* @param bool $not
|
||||
* @return void
|
||||
*/
|
||||
private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
|
||||
{
|
||||
if (0 === count($paths)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$command->add($not ? '-not' : null)->cmd('(');
|
||||
|
||||
foreach ($paths as $i => $path) {
|
||||
$expr = Expression::create($path);
|
||||
|
||||
// Fixes 'not search' regex problems.
|
||||
if ($expr->isRegex()) {
|
||||
$regex = $expr->getRegex();
|
||||
$regex->prepend($regex->hasStartFlag() ? '' : '.*')->setEndJoker(!$regex->hasEndFlag());
|
||||
} else {
|
||||
$expr->prepend('*')->append('*');
|
||||
}
|
||||
|
||||
$command
|
||||
->add($i > 0 ? '-or' : null)
|
||||
->add($expr->isRegex()
|
||||
? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
|
||||
: ($expr->isCaseSensitive() ? '-path' : '-ipath')
|
||||
)
|
||||
->arg($expr->prepend($dir.DIRECTORY_SEPARATOR)->renderPattern());
|
||||
}
|
||||
|
||||
$command->cmd(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Command $command
|
||||
* @param NumberComparator[] $sizes
|
||||
|
@ -73,6 +73,10 @@ class PhpAdapter extends AbstractAdapter
|
||||
$iterator = $iteratorAggregate->getIterator();
|
||||
}
|
||||
|
||||
if ($this->paths || $this->notPaths) {
|
||||
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
|
||||
}
|
||||
|
||||
return $iterator;
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,26 @@ class Expression implements ValueInterface
|
||||
return $this->value->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepend($expr)
|
||||
{
|
||||
$this->value->prepend($expr);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function append($expr)
|
||||
{
|
||||
$this->value->append($expr);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -61,6 +61,26 @@ class Glob implements ValueInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepend($expr)
|
||||
{
|
||||
$this->pattern = $expr.$this->pattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function append($expr)
|
||||
{
|
||||
$this->pattern .= $expr;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $strictLeadingDot
|
||||
* @param bool $strictWildcardSlash
|
||||
|
@ -66,7 +66,7 @@ class Regex implements ValueInterface
|
||||
$end = substr($m[1], -1);
|
||||
|
||||
if (($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start)) || ($start === '{' && $end === '}')) {
|
||||
return new self(substr($m[1], 1, -1), $m[2]);
|
||||
return new self(substr($m[1], 1, -1), $m[2], $end);
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,9 +76,15 @@ class Regex implements ValueInterface
|
||||
/**
|
||||
* @param string $pattern
|
||||
* @param string $options
|
||||
* @param string $delimiter
|
||||
*/
|
||||
public function __construct($pattern, $options = '')
|
||||
public function __construct($pattern, $options = '', $delimiter = null)
|
||||
{
|
||||
if (null !== $delimiter) {
|
||||
// removes delimiter escaping
|
||||
$pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
|
||||
}
|
||||
|
||||
$this->parsePattern($pattern);
|
||||
$this->options = $options;
|
||||
}
|
||||
@ -109,7 +115,7 @@ class Regex implements ValueInterface
|
||||
{
|
||||
return ($this->startFlag ? self::START_FLAG : '')
|
||||
.($this->startJoker ? self::JOKER : '')
|
||||
.$this->pattern
|
||||
.str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
|
||||
.($this->endJoker ? self::JOKER : '')
|
||||
.($this->endFlag ? self::END_FLAG : '');
|
||||
}
|
||||
@ -130,6 +136,26 @@ class Regex implements ValueInterface
|
||||
return Expression::TYPE_REGEX;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepend($expr)
|
||||
{
|
||||
$this->pattern = $expr.$this->pattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function append($expr)
|
||||
{
|
||||
$this->pattern .= $expr;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $option
|
||||
*
|
||||
@ -246,30 +272,6 @@ class Regex implements ValueInterface
|
||||
return $this->endJoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expr
|
||||
*
|
||||
* @return Regex
|
||||
*/
|
||||
public function prepend($expr)
|
||||
{
|
||||
$this->pattern = $expr.$this->pattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expr
|
||||
*
|
||||
* @return Regex
|
||||
*/
|
||||
public function append($expr)
|
||||
{
|
||||
$this->pattern .= $expr;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $replacements
|
||||
*
|
||||
|
@ -43,4 +43,18 @@ interface ValueInterface
|
||||
* @return int
|
||||
*/
|
||||
function getType();
|
||||
|
||||
/**
|
||||
* @param string $expr
|
||||
*
|
||||
* @return ValueInterface
|
||||
*/
|
||||
public function prepend($expr);
|
||||
|
||||
/**
|
||||
* @param string $expr
|
||||
*
|
||||
* @return ValueInterface
|
||||
*/
|
||||
public function append($expr);
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
private $contains = array();
|
||||
private $notContains = array();
|
||||
private $adapters = array();
|
||||
private $paths = array();
|
||||
private $notPaths = array();
|
||||
|
||||
private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
|
||||
|
||||
@ -283,6 +285,52 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds rules that filenames must match.
|
||||
*
|
||||
* You can use patterns (delimited with / sign) or simple strings.
|
||||
*
|
||||
* $finder->path('some/special/dir')
|
||||
* $finder->path('/some\/special\/dir/') // same as above
|
||||
*
|
||||
* Use only / as dirname separator.
|
||||
*
|
||||
* @param string $pattern A pattern (a regexp or a string)
|
||||
*
|
||||
* @return Finder The current Finder instance
|
||||
*
|
||||
* @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
|
||||
*/
|
||||
public function path($pattern)
|
||||
{
|
||||
$this->paths[] = $pattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds rules that filenames must not match.
|
||||
*
|
||||
* You can use patterns (delimited with / sign) or simple strings.
|
||||
*
|
||||
* $finder->notPath('some/special/dir')
|
||||
* $finder->notPath('/some\/special\/dir/') // same as above
|
||||
*
|
||||
* Use only / as dirname separator.
|
||||
*
|
||||
* @param string $pattern A pattern (a regexp or a string)
|
||||
*
|
||||
* @return Finder The current Finder instance
|
||||
*
|
||||
* @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
|
||||
*/
|
||||
public function notPath($pattern)
|
||||
{
|
||||
$this->notPaths[] = $pattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tests for file sizes.
|
||||
*
|
||||
@ -682,6 +730,8 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
->setSizes($this->sizes)
|
||||
->setDates($this->dates)
|
||||
->setFilters($this->filters)
|
||||
->setSort($this->sort);
|
||||
->setSort($this->sort)
|
||||
->setPath($this->paths)
|
||||
->setNotPath($this->notPaths);
|
||||
}
|
||||
}
|
||||
|
75
src/Symfony/Component/Finder/Iterator/PathFilterIterator.php
Normal file
75
src/Symfony/Component/Finder/Iterator/PathFilterIterator.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Finder\Iterator;
|
||||
|
||||
/**
|
||||
* PathFilterIterator filters files by path patterns (e.g. some/special/dir).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
|
||||
*/
|
||||
class PathFilterIterator extends MultiplePcreFilterIterator
|
||||
{
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return Boolean true if the value should be kept, false otherwise
|
||||
*/
|
||||
public function accept()
|
||||
{
|
||||
$filename = $this->current()->getRelativePathname();
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
|
||||
$filename = strtr($filename, '\\', '/');
|
||||
}
|
||||
|
||||
// should at least not match one rule to exclude
|
||||
foreach ($this->noMatchRegexps as $regex) {
|
||||
if (preg_match($regex, $filename)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// should at least match one rule
|
||||
$match = true;
|
||||
if ($this->matchRegexps) {
|
||||
$match = false;
|
||||
foreach ($this->matchRegexps as $regex) {
|
||||
if (preg_match($regex, $filename)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts strings to regexp.
|
||||
*
|
||||
* PCRE patterns are left unchanged.
|
||||
*
|
||||
* Default conversion:
|
||||
* 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/'
|
||||
*
|
||||
* Use only / as directory separator (on Windows also).
|
||||
*
|
||||
* @param string $str Pattern: regexp or dirname.
|
||||
*
|
||||
* @return string regexp corresponding to a given string or regexp
|
||||
*/
|
||||
protected function toRegex($str)
|
||||
{
|
||||
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
|
||||
}
|
||||
}
|
@ -602,7 +602,6 @@ class FinderTest extends Iterator\RealIteratorTestCase
|
||||
array('dolor sit amet', '@^L@m', array('dolor.txt', 'ipsum.txt')),
|
||||
array('/^lorem ipsum dolor sit amet$/m', 'foobar', array('lorem.txt')),
|
||||
array('lorem', 'foobar', array('lorem.txt')),
|
||||
|
||||
array('', 'lorem', array('dolor.txt', 'ipsum.txt')),
|
||||
array('ipsum dolor sit amet', '/^IPSUM/m', array('lorem.txt')),
|
||||
);
|
||||
@ -646,4 +645,59 @@ class FinderTest extends Iterator\RealIteratorTestCase
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getTestPathData
|
||||
*/
|
||||
public function testPath(Adapter\AdapterInterface $adapter, $matchPatterns, $noMatchPatterns, $expected)
|
||||
{
|
||||
$finder = $this->buildFinder($adapter);
|
||||
$finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures')
|
||||
->path($matchPatterns)
|
||||
->notPath($noMatchPatterns);
|
||||
|
||||
$this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
|
||||
}
|
||||
|
||||
public function getTestPathData()
|
||||
{
|
||||
$tests = array(
|
||||
array('', '', array()),
|
||||
array('/^A\/B\/C/', '/C$/',
|
||||
array('A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat')
|
||||
),
|
||||
array('/^A\/B/', 'foobar',
|
||||
array(
|
||||
'A'.DIRECTORY_SEPARATOR.'B',
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
|
||||
)
|
||||
),
|
||||
array('A/B/C', 'foobar',
|
||||
array(
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
|
||||
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
|
||||
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy',
|
||||
)
|
||||
),
|
||||
array('A/B', 'foobar',
|
||||
array(
|
||||
//dirs
|
||||
'A'.DIRECTORY_SEPARATOR.'B',
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
|
||||
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B',
|
||||
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
|
||||
//files
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
|
||||
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
|
||||
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat.copy',
|
||||
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy',
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
return $this->buildTestData($tests);
|
||||
}
|
||||
}
|
||||
|
0
src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat
Normal file
0
src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat
Normal file
@ -12,6 +12,8 @@
|
||||
namespace Symfony\Component\Finder\Tests\Iterator;
|
||||
|
||||
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
|
||||
use Symfony\Component\Finder\Tests\Iterator\MockSplFileInfo;
|
||||
use Symfony\Component\Finder\Tests\Iterator\MockFileListIterator;
|
||||
|
||||
class FilecontentFilterIteratorTest extends IteratorTestCase
|
||||
{
|
||||
@ -85,107 +87,3 @@ class FilecontentFilterIteratorTest extends IteratorTestCase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MockSplFileInfo extends \SplFileInfo
|
||||
{
|
||||
const TYPE_DIRECTORY = 1;
|
||||
const TYPE_FILE = 2;
|
||||
const TYPE_UNKNOWN = 3;
|
||||
|
||||
private $contents = null;
|
||||
private $mode = null;
|
||||
private $type = null;
|
||||
|
||||
public function __construct($param)
|
||||
{
|
||||
if (is_string($param)) {
|
||||
parent::__construct($param);
|
||||
} elseif (is_array($param)) {
|
||||
|
||||
$defaults = array(
|
||||
'name' => 'file.txt',
|
||||
'contents' => null,
|
||||
'mode' => null,
|
||||
'type' => null
|
||||
);
|
||||
$defaults = array_merge($defaults, $param);
|
||||
parent::__construct($defaults['name']);
|
||||
$this->setContents($defaults['contents']);
|
||||
$this->setMode($defaults['mode']);
|
||||
$this->setType($defaults['type']);
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('Incorrect parameter "%s"', $param));
|
||||
}
|
||||
}
|
||||
|
||||
public function isFile()
|
||||
{
|
||||
if ($this->type === null) {
|
||||
return preg_match('/file/', $this->getFilename());
|
||||
};
|
||||
|
||||
return self::TYPE_FILE === $this->type;
|
||||
}
|
||||
|
||||
public function isDir()
|
||||
{
|
||||
if ($this->type === null) {
|
||||
return preg_match('/directory/', $this->getFilename());
|
||||
}
|
||||
|
||||
return self::TYPE_DIRECTORY === $this->type;
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
if ($this->mode === null) {
|
||||
return preg_match('/r\+/', $this->getFilename());
|
||||
}
|
||||
|
||||
return preg_match('/r\+/', $this->mode);
|
||||
}
|
||||
|
||||
public function getContents()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
public function setContents($contents)
|
||||
{
|
||||
$this->contents = $contents;
|
||||
}
|
||||
|
||||
public function setMode($mode)
|
||||
{
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
if (is_string($type)) {
|
||||
switch ($type) {
|
||||
case 'directory':
|
||||
case 'd':
|
||||
$this->type = self::TYPE_DIRECTORY;
|
||||
break;
|
||||
case 'file':
|
||||
case 'f':
|
||||
$this->type = self::TYPE_FILE;
|
||||
break;
|
||||
default:
|
||||
$this->type = self::TYPE_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
$this->type = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MockFileListIterator extends \ArrayIterator
|
||||
{
|
||||
public function __construct(array $filesArray = array())
|
||||
{
|
||||
$files = array_map(function($file){ return new MockSplFileInfo($file); }, $filesArray);
|
||||
parent::__construct($files);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Finder\Tests\Iterator;
|
||||
|
||||
class MockFileListIterator extends \ArrayIterator
|
||||
{
|
||||
public function __construct(array $filesArray = array())
|
||||
{
|
||||
$files = array_map(function($file){ return new MockSplFileInfo($file); }, $filesArray);
|
||||
parent::__construct($files);
|
||||
}
|
||||
}
|
136
src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php
Normal file
136
src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Finder\Tests\Iterator;
|
||||
|
||||
class MockSplFileInfo extends \SplFileInfo
|
||||
{
|
||||
const TYPE_DIRECTORY = 1;
|
||||
const TYPE_FILE = 2;
|
||||
const TYPE_UNKNOWN = 3;
|
||||
|
||||
private $contents = null;
|
||||
private $mode = null;
|
||||
private $type = null;
|
||||
private $relativePath = null;
|
||||
private $relativePathname = null;
|
||||
|
||||
public function __construct($param)
|
||||
{
|
||||
if (is_string($param)) {
|
||||
parent::__construct($param);
|
||||
} elseif (is_array($param)) {
|
||||
|
||||
$defaults = array(
|
||||
'name' => 'file.txt',
|
||||
'contents' => null,
|
||||
'mode' => null,
|
||||
'type' => null,
|
||||
'relativePath' => null,
|
||||
'relativePathname' => null,
|
||||
);
|
||||
$defaults = array_merge($defaults, $param);
|
||||
parent::__construct($defaults['name']);
|
||||
$this->setContents($defaults['contents']);
|
||||
$this->setMode($defaults['mode']);
|
||||
$this->setType($defaults['type']);
|
||||
$this->setRelativePath($defaults['relativePath']);
|
||||
$this->setRelativePathname($defaults['relativePathname']);
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('Incorrect parameter "%s"', $param));
|
||||
}
|
||||
}
|
||||
|
||||
public function isFile()
|
||||
{
|
||||
if ($this->type === null) {
|
||||
return preg_match('/file/', $this->getFilename());
|
||||
};
|
||||
|
||||
return self::TYPE_FILE === $this->type;
|
||||
}
|
||||
|
||||
public function isDir()
|
||||
{
|
||||
if ($this->type === null) {
|
||||
return preg_match('/directory/', $this->getFilename());
|
||||
}
|
||||
|
||||
return self::TYPE_DIRECTORY === $this->type;
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
if ($this->mode === null) {
|
||||
return preg_match('/r\+/', $this->getFilename());
|
||||
}
|
||||
|
||||
return preg_match('/r\+/', $this->mode);
|
||||
}
|
||||
|
||||
public function getContents()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
public function setContents($contents)
|
||||
{
|
||||
$this->contents = $contents;
|
||||
}
|
||||
|
||||
public function setMode($mode)
|
||||
{
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
if (is_string($type)) {
|
||||
switch ($type) {
|
||||
case 'directory':
|
||||
$this->type = self::TYPE_DIRECTORY;
|
||||
case 'd':
|
||||
$this->type = self::TYPE_DIRECTORY;
|
||||
break;
|
||||
case 'file':
|
||||
$this->type = self::TYPE_FILE;
|
||||
case 'f':
|
||||
$this->type = self::TYPE_FILE;
|
||||
break;
|
||||
default:
|
||||
$this->type = self::TYPE_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
$this->type = $type;
|
||||
}
|
||||
}
|
||||
|
||||
public function setRelativePath($relativePath)
|
||||
{
|
||||
$this->relativePath = $relativePath;
|
||||
}
|
||||
|
||||
public function setRelativePathname($relativePathname)
|
||||
{
|
||||
$this->relativePathname = $relativePathname;
|
||||
}
|
||||
|
||||
public function getRelativePath()
|
||||
{
|
||||
return $this->relativePath;
|
||||
}
|
||||
|
||||
public function getRelativePathname()
|
||||
{
|
||||
return $this->relativePathname;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Finder\Tests\Iterator;
|
||||
|
||||
use Symfony\Component\Finder\Iterator\PathFilterIterator;
|
||||
|
||||
class PathFilterIteratorTest extends IteratorTestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @dataProvider getTestFilterData
|
||||
*/
|
||||
public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)
|
||||
{
|
||||
$iterator = new PathFilterIterator($inner, $matchPatterns, $noMatchPatterns);
|
||||
$this->assertIterator($resultArray, $iterator);
|
||||
}
|
||||
|
||||
public function getTestFilterData()
|
||||
{
|
||||
$inner = new MockFileListIterator();
|
||||
|
||||
//PATH: A/B/C/abc.dat
|
||||
$inner[] = new MockSplFileInfo(array(
|
||||
'name' => 'abc.dat',
|
||||
'relativePathname' => 'A' . DIRECTORY_SEPARATOR . 'B' . DIRECTORY_SEPARATOR . 'C' . DIRECTORY_SEPARATOR . 'abc.dat',
|
||||
));
|
||||
|
||||
//PATH: A/B/ab.dat
|
||||
$inner[] = new MockSplFileInfo(array(
|
||||
'name' => 'ab.dat',
|
||||
'relativePathname' => 'A' . DIRECTORY_SEPARATOR . 'B' . DIRECTORY_SEPARATOR . 'ab.dat',
|
||||
));
|
||||
|
||||
//PATH: A/a.dat
|
||||
$inner[] = new MockSplFileInfo(array(
|
||||
'name' => 'a.dat',
|
||||
'relativePathname' => 'A' . DIRECTORY_SEPARATOR . 'a.dat',
|
||||
));
|
||||
|
||||
//PATH: copy/A/B/C/abc.dat.copy
|
||||
$inner[] = new MockSplFileInfo(array(
|
||||
'name' => 'abc.dat.copy',
|
||||
'relativePathname' => 'copy' . DIRECTORY_SEPARATOR . 'A' . DIRECTORY_SEPARATOR . 'B' . DIRECTORY_SEPARATOR . 'C' . DIRECTORY_SEPARATOR . 'abc.dat',
|
||||
));
|
||||
|
||||
//PATH: copy/A/B/ab.dat.copy
|
||||
$inner[] = new MockSplFileInfo(array(
|
||||
'name' => 'ab.dat.copy',
|
||||
'relativePathname' => 'copy' . DIRECTORY_SEPARATOR . 'A' . DIRECTORY_SEPARATOR . 'B' . DIRECTORY_SEPARATOR . 'ab.dat',
|
||||
));
|
||||
|
||||
//PATH: copy/A/a.dat.copy
|
||||
$inner[] = new MockSplFileInfo(array(
|
||||
'name' => 'a.dat.copy',
|
||||
'relativePathname' => 'copy' . DIRECTORY_SEPARATOR . 'A' . DIRECTORY_SEPARATOR . 'a.dat',
|
||||
));
|
||||
|
||||
return array(
|
||||
array($inner, array('/^A/'), array(), array('abc.dat', 'ab.dat', 'a.dat')),
|
||||
array($inner, array('/^A\/B/'), array(), array('abc.dat', 'ab.dat')),
|
||||
array($inner, array('/^A\/B\/C/'), array(), array('abc.dat')),
|
||||
array($inner, array('/A\/B\/C/'), array(), array('abc.dat', 'abc.dat.copy')),
|
||||
|
||||
array($inner, array('A'), array(), array('abc.dat', 'ab.dat', 'a.dat', 'abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')),
|
||||
array($inner, array('A/B'), array(), array('abc.dat', 'ab.dat', 'abc.dat.copy', 'ab.dat.copy')),
|
||||
array($inner, array('A/B/C'), array(), array('abc.dat', 'abc.dat.copy')),
|
||||
|
||||
array($inner, array('copy/A'), array(), array('abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')),
|
||||
array($inner, array('copy/A/B'), array(), array('abc.dat.copy', 'ab.dat.copy')),
|
||||
array($inner, array('copy/A/B/C'), array(), array('abc.dat.copy')),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user