diff --git a/src/Symfony/Components/Finder/Comparator/Comparator.php b/src/Symfony/Components/Finder/Comparator/Comparator.php new file mode 100644 index 0000000000..747894186e --- /dev/null +++ b/src/Symfony/Components/Finder/Comparator/Comparator.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * Comparator. + * + * @package Symfony + * @subpackage Components_Finder + * @author Fabien Potencier PHP port + */ +class Comparator +{ + protected $target; + protected $operator; + + public function setTarget($target) + { + $this->target = $target; + } + + public function setOperator($operator) + { + if (!$operator) { + $operator = '=='; + } + + if (!in_array($operator, array('>', '<', '>=', '<=', '=='))) { + throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); + } + + $this->operator = $operator; + } + + /** + * Tests against the target. + * + * @param mixed $test A test value + */ + public function test($test) + { + switch ($this->operator) { + case '>': + return $test > $this->target; + case '>=': + return $test >= $this->target; + case '<': + return $test < $this->target; + case '<=': + return $test <= $this->target; + } + + return $test == $this->target; + } +} diff --git a/src/Symfony/Components/Finder/Comparator/DateComparator.php b/src/Symfony/Components/Finder/Comparator/DateComparator.php new file mode 100644 index 0000000000..791adbeaa7 --- /dev/null +++ b/src/Symfony/Components/Finder/Comparator/DateComparator.php @@ -0,0 +1,57 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * DateCompare compiles date comparisons. + * + * @package Symfony + * @subpackage Components_Finder + * @author Fabien Potencier PHP port + */ +class DateComparator extends Comparator +{ + protected $target; + protected $comparison; + + /** + * Constructor. + * + * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct($test) + { + if (!preg_match('#^\s*([<>=]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); + } + + if (false === $target = @strtotime($matches[2])) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); + } + + $operator = isset($matches[1]) ? $matches[1] : '=='; + if ('since' === $operator || 'after' === $operator) + { + $operator = '>'; + } + + if ('until' === $operator || 'before' === $operator) + { + $operator = '<'; + } + + $this->setOperator($operator); + $this->setTarget($target); + } +} diff --git a/src/Symfony/Components/Finder/NumberCompare.php b/src/Symfony/Components/Finder/Comparator/NumberComparator.php similarity index 65% rename from src/Symfony/Components/Finder/NumberCompare.php rename to src/Symfony/Components/Finder/Comparator/NumberComparator.php index 39f5dc7cb3..536eaa1f90 100644 --- a/src/Symfony/Components/Finder/NumberCompare.php +++ b/src/Symfony/Components/Finder/Comparator/NumberComparator.php @@ -1,6 +1,6 @@ * @see http://physics.nist.gov/cuu/Units/binary.html */ -class NumberCompare +class NumberComparator extends Comparator { - protected $target; - protected $comparison; - /** * Constructor. * * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood */ 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)); + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); } - $this->target = $matches[2]; - $this->comparison = isset($matches[1]) ? $matches[1] : '=='; - + $target = $matches[2]; $magnitude = strtolower(isset($matches[3]) ? $matches[3] : ''); switch ($magnitude) { case 'k': - $this->target *= 1000; + $target *= 1000; break; case 'ki': - $this->target *= 1024; + $target *= 1024; break; case 'm': - $this->target *= 1000000; + $target *= 1000000; break; case 'mi': - $this->target *= 1024*1024; + $target *= 1024*1024; break; case 'g': - $this->target *= 1000000000; + $target *= 1000000000; break; case 'gi': - $this->target *= 1024*1024*1024; + $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); + $this->setTarget($target); + $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); } } diff --git a/src/Symfony/Components/Finder/Finder.php b/src/Symfony/Components/Finder/Finder.php index 9db0515b47..6aed469068 100644 --- a/src/Symfony/Components/Finder/Finder.php +++ b/src/Symfony/Components/Finder/Finder.php @@ -43,8 +43,7 @@ class Finder implements \IteratorAggregate protected $sort = false; protected $ignoreVCS = true; protected $dirs = array(); - protected $minDate = false; - protected $maxDate = false; + protected $dates = array(); /** * Restricts the matching to directories only. @@ -107,46 +106,26 @@ class Finder implements \IteratorAggregate } /** - * Sets the maximum date (last modified) for a file or directory. + * Adds tests for file dates (last modified). * * The date must be something that strtotime() is able to parse: * - * $finder->maxDate('yesterday'); - * $finder->maxDate('2 days ago'); - * $finder->maxDate('now - 2 hours'); - * $finder->maxDate('2005-10-15'); + * $finder->date('since yesterday'); + * $finder->date('until 2 days ago'); + * $finder->date('> now - 2 hours'); + * $finder->date('>= 2005-10-15'); * - * @param string $date A date + * @param string $date A date rage string * * @return Symfony\Components\Finder The current Finder instance * + * @see strtotime * @see Symfony\Components\Finder\Iterator\DateRangeFilterIterator + * @see Symfony\Components\Finder\Comparator\DateComparator */ - public function maxDate($date) + public function date($date) { - if (false === $this->maxDate = @strtotime($date)) { - throw new \InvalidArgumentException(sprintf('"%s" is not a valid date')); - } - - return $this; - } - - /** - * Sets the minimum date (last modified) for a file or a directory. - * - * The date must be something that strtotime() is able to parse (@see maxDate()). - * - * @param string $date A date - * - * @return Symfony\Components\Finder The current Finder instance - * - * @see Symfony\Components\Finder\Iterator\DateRangeFilterIterator - */ - public function minDate($date) - { - if (false === $this->minDate = @strtotime($date)) { - throw new \InvalidArgumentException(sprintf('"%s" is not a valid date')); - } + $this->dates[] = new Comparator\DateComparator($date); return $this; } @@ -201,11 +180,11 @@ class Finder implements \IteratorAggregate * @return Symfony\Components\Finder The current Finder instance * * @see Symfony\Components\Finder\Iterator\SizeRangeFilterIterator - * @see Symfony\Components\Finder\NumberCompare + * @see Symfony\Components\Finder\Comparator\NumberComparator */ public function size($size) { - $this->sizes[] = new NumberCompare($size); + $this->sizes[] = new Comparator\NumberComparator($size); return $this; } @@ -410,8 +389,8 @@ class Finder implements \IteratorAggregate $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); } - if (false !== $this->minDate || false !== $this->maxDate) { - $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->minDate, $this->maxDate); + if ($this->dates) { + $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); } if ($this->filters) { diff --git a/src/Symfony/Components/Finder/Iterator/DateRangeFilterIterator.php b/src/Symfony/Components/Finder/Iterator/DateRangeFilterIterator.php index fdf6d5f057..80065d28a5 100644 --- a/src/Symfony/Components/Finder/Iterator/DateRangeFilterIterator.php +++ b/src/Symfony/Components/Finder/Iterator/DateRangeFilterIterator.php @@ -20,20 +20,17 @@ namespace Symfony\Components\Finder\Iterator; */ class DateRangeFilterIterator extends \FilterIterator { - protected $minDate = false; - protected $maxDate = false; + protected $patterns = array(); /** * Constructor. * * @param \Iterator $iterator The Iterator to filter - * @param integer $minDate The minimum date - * @param integer $maxDate The maximum date + * @param array $patterns An array of \DateCompare instances */ - public function __construct(\Iterator $iterator, $minDate = false, $maxDate = false) + public function __construct(\Iterator $iterator, array $patterns) { - $this->minDate = $minDate; - $this->maxDate = $maxDate; + $this->patterns = $patterns; parent::__construct($iterator); } @@ -52,13 +49,10 @@ class DateRangeFilterIterator extends \FilterIterator } $filedate = $fileinfo->getMTime(); - - if ( - (false !== $this->minDate && $filedate < $this->minDate) - || - (false !== $this->maxDate && $filedate > $this->maxDate) - ) { - return false; + foreach ($this->patterns as $compare) { + if (!$compare->test($filedate)) { + return false; + } } return true; diff --git a/tests/Symfony/Tests/Components/Finder/Comparator/ComparatorTest.php b/tests/Symfony/Tests/Components/Finder/Comparator/ComparatorTest.php new file mode 100644 index 0000000000..53b8c5fdbe --- /dev/null +++ b/tests/Symfony/Tests/Components/Finder/Comparator/ComparatorTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Components\Finder\Comparator; + +use Symfony\Components\Finder\Comparator\Comparator; + +class ComparatorTest extends \PHPUnit_Framework_TestCase +{ + public function testSetOperator() + { + $comparator = new Comparator(); + try { + $comparator->setOperator('foo'); + $this->fail('->setOperator() throws an \InvalidArgumentException if the operator is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->setOperator() throws an \InvalidArgumentException if the operator is not valid.'); + } + } + + /** + * @dataProvider getTestData + */ + public function testTest($operator, $target, $match, $noMatch) + { + $c = new Comparator(); + $c->setOperator($operator); + $c->setTarget($target); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $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')), + ); + } +} diff --git a/tests/Symfony/Tests/Components/Finder/Comparator/DateComparatorTest.php b/tests/Symfony/Tests/Components/Finder/Comparator/DateComparatorTest.php new file mode 100644 index 0000000000..5564cc2957 --- /dev/null +++ b/tests/Symfony/Tests/Components/Finder/Comparator/DateComparatorTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Components\Finder\Comparator; + +use Symfony\Components\Finder\Comparator\DateComparator; + +class DateComparatorTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + try { + new DateComparator('foobar'); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + + try { + new DateComparator(''); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + } + + /** + * @dataProvider getTestData + */ + public function testTest($test, $match, $noMatch) + { + $c = new DateComparator($test); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('< 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('until 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('before 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('> 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('after 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('since 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + ); + + } +} diff --git a/tests/Symfony/Tests/Components/Finder/NumberCompareTest.php b/tests/Symfony/Tests/Components/Finder/Comparator/NumberComparatorTest.php similarity index 81% rename from tests/Symfony/Tests/Components/Finder/NumberCompareTest.php rename to tests/Symfony/Tests/Components/Finder/Comparator/NumberComparatorTest.php index fc256b76b4..7e1e315b2b 100644 --- a/tests/Symfony/Tests/Components/Finder/NumberCompareTest.php +++ b/tests/Symfony/Tests/Components/Finder/Comparator/NumberComparatorTest.php @@ -9,19 +9,19 @@ * file that was distributed with this source code. */ -namespace Symfony\Tests\Components\Finder; +namespace Symfony\Tests\Components\Finder\Comparator; -use Symfony\Components\Finder\NumberCompare; +use Symfony\Components\Finder\Comparator\NumberComparator; -class NumberCompareTest extends \PHPUnit_Framework_TestCase +class NumberComparatorTest extends \PHPUnit_Framework_TestCase { public function testConstructor() { try { - new NumberCompare('foobar'); - $this->fail('->test() throws an \InvalidArgumentException if the test expression is not valid.'); + new NumberComparator('foobar'); + $this->fail('__construct() 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.'); + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); } } @@ -30,13 +30,13 @@ class NumberCompareTest extends \PHPUnit_Framework_TestCase */ public function testTest($test, $match, $noMatch) { + $c = new NumberComparator($test); + 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'); } } diff --git a/tests/Symfony/Tests/Components/Finder/FinderTest.php b/tests/Symfony/Tests/Components/Finder/FinderTest.php index d4a3dda84c..ff0d49dead 100644 --- a/tests/Symfony/Tests/Components/Finder/FinderTest.php +++ b/tests/Symfony/Tests/Components/Finder/FinderTest.php @@ -112,6 +112,13 @@ class FinderTest extends Iterator\RealIteratorTestCase $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); } + public function testDate() + { + $finder = new Finder(); + $this->assertSame($finder, $finder->files()->date('until last month')); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + public function testExclude() { $finder = new Finder(); @@ -182,7 +189,7 @@ class FinderTest extends Iterator\RealIteratorTestCase $finder = new Finder(); $iterator = $finder->files()->name('*.php')->maxDepth(0)->in(array(self::$tmpDir, __DIR__))->getIterator(); - $this->assertIterator(array(self::$tmpDir.'test.php', __DIR__.'/FinderTest.php', __DIR__.'/GlobTest.php', __DIR__.'/NumberCompareTest.php'), $iterator); + $this->assertIterator(array(self::$tmpDir.'test.php', __DIR__.'/FinderTest.php', __DIR__.'/GlobTest.php'), $iterator); } public function testGetIterator() diff --git a/tests/Symfony/Tests/Components/Finder/Iterator/DateRangeFilterIteratorTest.php b/tests/Symfony/Tests/Components/Finder/Iterator/DateRangeFilterIteratorTest.php new file mode 100644 index 0000000000..b520501a79 --- /dev/null +++ b/tests/Symfony/Tests/Components/Finder/Iterator/DateRangeFilterIteratorTest.php @@ -0,0 +1,41 @@ + + * + * 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\DateRangeFilterIterator; +use Symfony\Components\Finder\Comparator\DateComparator; + +require_once __DIR__.'/RealIteratorTestCase.php'; + +class DateRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($size, $expected) + { + $inner = new Iterator(self::$files); + + $iterator = new DateRangeFilterIterator($inner, $size); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + return array( + array(array(new DateComparator('since 20 years ago')), 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(array(new DateComparator('since 2 months ago')), 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/toto')), + array(array(new DateComparator('until last month')), 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/toto')), + ); + } +} diff --git a/tests/Symfony/Tests/Components/Finder/Iterator/RealIteratorTestCase.php b/tests/Symfony/Tests/Components/Finder/Iterator/RealIteratorTestCase.php index 8543820420..e4c3add1db 100644 --- a/tests/Symfony/Tests/Components/Finder/Iterator/RealIteratorTestCase.php +++ b/tests/Symfony/Tests/Components/Finder/Iterator/RealIteratorTestCase.php @@ -20,7 +20,14 @@ class RealIteratorTestCase extends IteratorTestCase 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'); + 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(); @@ -29,24 +36,27 @@ class RealIteratorTestCase extends IteratorTestCase mkdir($tmpDir); foreach (self::$files as $file) { - if (false !== ($pos = strpos($file, '.')) && '/' !== $file[$pos - 1]) { - touch($file); - } else { + if ('/' === $file[strlen($file) - 1]) { mkdir($file); + } else { + touch($file); } } file_put_contents($tmpDir.'/test.php', str_repeat(' ', 800)); file_put_contents($tmpDir.'/test.py', str_repeat(' ', 2000)); + + touch(sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp', strtotime('2005-10-15')); + touch(sys_get_temp_dir().'/symfony2_finder/test.php', strtotime('2005-10-15')); } static public function tearDownAfterClass() { - foreach (self::$files as $file) { - if (false !== ($pos = strpos($file, '.')) && '/' !== $file[$pos - 1]) { - @unlink($file); - } else { + foreach (array_reverse(self::$files) as $file) { + if ('/' === $file[strlen($file) - 1]) { @rmdir($file); + } else { + @unlink($file); } } } diff --git a/tests/Symfony/Tests/Components/Finder/Iterator/SizeRangeFilterIteratorTest.php b/tests/Symfony/Tests/Components/Finder/Iterator/SizeRangeFilterIteratorTest.php index 4db039b682..de2b27646d 100644 --- a/tests/Symfony/Tests/Components/Finder/Iterator/SizeRangeFilterIteratorTest.php +++ b/tests/Symfony/Tests/Components/Finder/Iterator/SizeRangeFilterIteratorTest.php @@ -12,7 +12,7 @@ namespace Symfony\Tests\Components\Finder\Iterator; use Symfony\Components\Finder\Iterator\SizeRangeFilterIterator; -use Symfony\Components\Finder\NumberCompare; +use Symfony\Components\Finder\Comparator\NumberComparator; require_once __DIR__.'/RealIteratorTestCase.php'; @@ -33,7 +33,7 @@ class SizeRangeFilterIteratorTest extends RealIteratorTestCase 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')), + array(array(new NumberComparator('< 1K'), new NumberComparator('> 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')), ); } }