From c36dfc16e6df2694867eece435a0b5a4018faa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C5=82odzimierz=20Gajda?= Date: Wed, 4 Jul 2012 08:13:41 +0200 Subject: [PATCH 1/4] [Component][Finder] ->path(), ->notPath() methods (with basic tests) --- .../Finder/Adapter/AbstractAdapter.php | 23 +++ .../Component/Finder/Adapter/PhpAdapter.php | 4 + src/Symfony/Component/Finder/Finder.php | 52 ++++++- .../Finder/Iterator/PathFilterIterator.php | 75 ++++++++++ .../Component/Finder/Tests/FinderTest.php | 61 ++++++++ .../Finder/Tests/Fixtures/A/B/C/abc.dat | 0 .../Finder/Tests/Fixtures/A/B/ab.dat | 0 .../Component/Finder/Tests/Fixtures/A/a.dat | 0 .../Tests/Fixtures/copy/A/B/C/abc.dat.copy | 0 .../Tests/Fixtures/copy/A/B/ab.dat.copy | 0 .../Finder/Tests/Fixtures/copy/A/a.dat.copy | 0 .../FilecontentFilterIteratorTest.php | 106 +------------- .../Tests/Iterator/MockFileListIterator.php | 21 +++ .../Finder/Tests/Iterator/MockSplFileInfo.php | 136 ++++++++++++++++++ .../Tests/Iterator/PathFilterIteratorTest.php | 86 +++++++++++ 15 files changed, 459 insertions(+), 105 deletions(-) create mode 100644 src/Symfony/Component/Finder/Iterator/PathFilterIterator.php create mode 100644 src/Symfony/Component/Finder/Tests/Fixtures/A/B/C/abc.dat create mode 100644 src/Symfony/Component/Finder/Tests/Fixtures/A/B/ab.dat create mode 100644 src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat create mode 100644 src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy create mode 100644 src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/ab.dat.copy create mode 100644 src/Symfony/Component/Finder/Tests/Fixtures/copy/A/a.dat.copy create mode 100644 src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.php create mode 100644 src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php create mode 100644 src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.php diff --git a/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php b/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php index 8958571bdff7b..be8c003437bbb 100644 --- a/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php @@ -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} @@ -171,4 +173,25 @@ public function setSort($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; + } + } diff --git a/src/Symfony/Component/Finder/Adapter/PhpAdapter.php b/src/Symfony/Component/Finder/Adapter/PhpAdapter.php index 2dbae76a565f7..3607165f9bd62 100644 --- a/src/Symfony/Component/Finder/Adapter/PhpAdapter.php +++ b/src/Symfony/Component/Finder/Adapter/PhpAdapter.php @@ -73,6 +73,10 @@ public function searchInDirectory($dir) $iterator = $iteratorAggregate->getIterator(); } + if ($this->paths || $this->notPaths) { + $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths); + } + return $iterator; } diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index b6d15176ff683..b6b85d5239d99 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -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 @@ public function notContains($pattern) 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 @@ private function buildAdapter(AdapterInterface $adapter) ->setSizes($this->sizes) ->setDates($this->dates) ->setFilters($this->filters) - ->setSort($this->sort); + ->setSort($this->sort) + ->setPath($this->paths) + ->setNotPath($this->notPaths); } } diff --git a/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php b/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php new file mode 100644 index 0000000000000..c736f0b310383 --- /dev/null +++ b/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php @@ -0,0 +1,75 @@ + + * + * 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 + * @author Włodzimierz Gajda + */ +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, '/').'/'; + } +} diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index e1b1779f99284..046cfed4afe0f 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -646,4 +646,65 @@ private function buildTestData(array $tests) return $data; } + + /** + * @dataProvider getTestPathData + */ + public function testPath($matchPatterns, $noMatchPatterns, $expected) + { + $finder = new Finder(); + $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures') + ->path($matchPatterns) + ->notPath($noMatchPatterns); + + $this->assertIterator($this->toAbsoluteFixtures($expected), $finder); + } + + public function getTestPathData() + { + return 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', + ) + ), + + + ); + } + } diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/A/B/C/abc.dat b/src/Symfony/Component/Finder/Tests/Fixtures/A/B/C/abc.dat new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/A/B/ab.dat b/src/Symfony/Component/Finder/Tests/Fixtures/A/B/ab.dat new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat b/src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy b/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/ab.dat.copy b/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/ab.dat.copy new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/a.dat.copy b/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/a.dat.copy new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.php index 5f0625f9f5e66..5f2b39823941e 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.php @@ -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 @@ public function getTestFilterData() ); } } - -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); - } -} diff --git a/src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.php b/src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.php new file mode 100644 index 0000000000000..6d8ae39561b11 --- /dev/null +++ b/src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.php @@ -0,0 +1,21 @@ + + * + * 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); + } +} diff --git a/src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php b/src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php new file mode 100644 index 0000000000000..50312860bd4b1 --- /dev/null +++ b/src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php @@ -0,0 +1,136 @@ + + * + * 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; + } + +} diff --git a/src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.php new file mode 100644 index 0000000000000..6a155d4d7cf01 --- /dev/null +++ b/src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.php @@ -0,0 +1,86 @@ + + * + * 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')), + + ); + } + +} + From 5c6dbebf0456f1d1d8c1d203290d79635b5ac8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Mon, 29 Oct 2012 19:55:27 +0100 Subject: [PATCH 2/4] [Finder] Fixed tests. --- .../Component/Finder/Tests/FinderTest.php | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 046cfed4afe0f..aa17469a1a665 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -602,7 +602,6 @@ public function getContainsTestData() 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')), ); @@ -650,9 +649,9 @@ private function buildTestData(array $tests) /** * @dataProvider getTestPathData */ - public function testPath($matchPatterns, $noMatchPatterns, $expected) + public function testPath(Adapter\AdapterInterface $adapter, $matchPatterns, $noMatchPatterns, $expected) { - $finder = new Finder(); + $finder = $this->buildFinder($adapter); $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures') ->path($matchPatterns) ->notPath($noMatchPatterns); @@ -662,13 +661,11 @@ public function testPath($matchPatterns, $noMatchPatterns, $expected) public function getTestPathData() { - return array( + $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', @@ -677,7 +674,6 @@ public function getTestPathData() 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat', ) ), - array('A/B/C', 'foobar', array( 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C', @@ -686,7 +682,6 @@ public function getTestPathData() 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy', ) ), - array('A/B', 'foobar', array( //dirs @@ -694,7 +689,6 @@ public function getTestPathData() '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', @@ -702,9 +696,8 @@ public function getTestPathData() 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy', ) ), - - ); - } + return $this->buildTestData($tests); + } } From 6258d1203ba39bdd611cc3cb94cab45ab9d42eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Mon, 29 Oct 2012 19:56:47 +0100 Subject: [PATCH 3/4] [Finder] Fixed expression classes. --- .../Finder/Expression/Expression.php | 20 +++++++ .../Component/Finder/Expression/Glob.php | 20 +++++++ .../Component/Finder/Expression/Regex.php | 56 ++++++++++--------- .../Finder/Expression/ValueInterface.php | 14 +++++ 4 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Component/Finder/Expression/Expression.php b/src/Symfony/Component/Finder/Expression/Expression.php index b8124124269d9..b08be159a56cb 100644 --- a/src/Symfony/Component/Finder/Expression/Expression.php +++ b/src/Symfony/Component/Finder/Expression/Expression.php @@ -86,6 +86,26 @@ public function getType() 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 */ diff --git a/src/Symfony/Component/Finder/Expression/Glob.php b/src/Symfony/Component/Finder/Expression/Glob.php index 5e7b2d01127fa..f6af7d01294b4 100644 --- a/src/Symfony/Component/Finder/Expression/Glob.php +++ b/src/Symfony/Component/Finder/Expression/Glob.php @@ -61,6 +61,26 @@ public function isCaseSensitive() 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 diff --git a/src/Symfony/Component/Finder/Expression/Regex.php b/src/Symfony/Component/Finder/Expression/Regex.php index 40d776394a891..d8eba7c0025b7 100644 --- a/src/Symfony/Component/Finder/Expression/Regex.php +++ b/src/Symfony/Component/Finder/Expression/Regex.php @@ -66,7 +66,7 @@ public static function create($expr) $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 @@ public static function create($expr) /** * @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 @@ public function renderPattern() { 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 @@ public function getType() 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 @@ public function hasEndJoker() 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 * diff --git a/src/Symfony/Component/Finder/Expression/ValueInterface.php b/src/Symfony/Component/Finder/Expression/ValueInterface.php index aca28f4bad894..37342b02e95b4 100644 --- a/src/Symfony/Component/Finder/Expression/ValueInterface.php +++ b/src/Symfony/Component/Finder/Expression/ValueInterface.php @@ -43,4 +43,18 @@ function isCaseSensitive(); * @return int */ function getType(); + + /** + * @param string $expr + * + * @return ValueInterface + */ + public function prepend($expr); + + /** + * @param string $expr + * + * @return ValueInterface + */ + public function append($expr); } From 4e21bf273d216f7ccbcb5ed168a519b7b512aa4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Mon, 29 Oct 2012 19:57:16 +0100 Subject: [PATCH 4/4] [Finder] Added path & notPath support to gnu find adapter. --- .../Finder/Adapter/AbstractAdapter.php | 25 +++++------ .../Finder/Adapter/AdapterInterface.php | 44 ++++++++++++------- .../Finder/Adapter/GnuFindAdapter.php | 42 +++++++++++++++++- 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php b/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php index be8c003437bbb..38ac2835fd358 100644 --- a/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php @@ -42,7 +42,7 @@ public function setFollowLinks($followLinks) $this->followLinks = $followLinks; return $this; - } + } /** * {@inheritdoc} @@ -52,7 +52,7 @@ public function setMode($mode) $this->mode = $mode; return $this; - } + } /** * {@inheritdoc} @@ -82,7 +82,7 @@ public function setDepths(array $depths) } return $this; - } + } /** * {@inheritdoc} @@ -92,7 +92,7 @@ public function setExclude(array $exclude) $this->exclude = $exclude; return $this; - } + } /** * {@inheritdoc} @@ -102,7 +102,7 @@ public function setNames(array $names) $this->names = $names; return $this; - } + } /** * {@inheritdoc} @@ -112,7 +112,7 @@ public function setNotNames(array $notNames) $this->notNames = $notNames; return $this; - } + } /** * {@inheritdoc} @@ -122,7 +122,7 @@ public function setContains(array $contains) $this->contains = $contains; return $this; - } + } /** * {@inheritdoc} @@ -132,7 +132,7 @@ public function setNotContains(array $notContains) $this->notContains = $notContains; return $this; - } + } /** * {@inheritdoc} @@ -142,7 +142,7 @@ public function setSizes(array $sizes) $this->sizes = $sizes; return $this; - } + } /** * {@inheritdoc} @@ -152,7 +152,7 @@ public function setDates(array $dates) $this->dates = $dates; return $this; - } + } /** * {@inheritdoc} @@ -162,7 +162,7 @@ public function setFilters(array $filters) $this->filters = $filters; return $this; - } + } /** * {@inheritdoc} @@ -172,7 +172,7 @@ public function setSort($sort) $this->sort = $sort; return $this; - } + } /** * {@inheritdoc} @@ -193,5 +193,4 @@ public function setNotPath(array $notPaths) return $this; } - } diff --git a/src/Symfony/Component/Finder/Adapter/AdapterInterface.php b/src/Symfony/Component/Finder/Adapter/AdapterInterface.php index 246a26cb06ad0..829297b3ecded 100644 --- a/src/Symfony/Component/Finder/Adapter/AdapterInterface.php +++ b/src/Symfony/Component/Finder/Adapter/AdapterInterface.php @@ -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(); } diff --git a/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.php b/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.php index 3a99307951559..06d47cca840f5 100644 --- a/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.php +++ b/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.php @@ -80,6 +80,8 @@ public function searchInDirectory($dir) $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 @@ private function buildNamesFiltering(Command $command, array $names, $not = fals 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 @@ private function buildNamesFiltering(Command $command, array $names, $not = fals $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