Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[Component][Finder] ->path(), ->notPath() methods (with basic tests)
  • Loading branch information
gajdaw committed Oct 29, 2012
1 parent 2a23dbd commit c36dfc1
Show file tree
Hide file tree
Showing 15 changed files with 459 additions and 105 deletions.
23 changes: 23 additions & 0 deletions src/Symfony/Component/Finder/Adapter/AbstractAdapter.php
Expand Up @@ -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}
Expand Down Expand Up @@ -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;
}

}
4 changes: 4 additions & 0 deletions src/Symfony/Component/Finder/Adapter/PhpAdapter.php
Expand Up @@ -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;
}

Expand Down
52 changes: 51 additions & 1 deletion src/Symfony/Component/Finder/Finder.php
Expand Up @@ -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');

Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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);
}
}
75 changes: 75 additions & 0 deletions src/Symfony/Component/Finder/Iterator/PathFilterIterator.php
@@ -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, '/').'/';
}
}
61 changes: 61 additions & 0 deletions src/Symfony/Component/Finder/Tests/FinderTest.php
Expand Up @@ -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',
)
),


);
}

}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Expand Up @@ -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
{
Expand Down Expand Up @@ -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);
}
}
@@ -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);
}
}

0 comments on commit c36dfc1

Please sign in to comment.