[Finder] refactored size and date comparators

This commit is contained in:
Fabien Potencier 2010-05-21 12:50:13 +02:00
parent efb5801ac1
commit 9426f820a7
12 changed files with 351 additions and 109 deletions

View File

@ -0,0 +1,64 @@
<?php
namespace Symfony\Components\Finder\Comparator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Comparator.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com> 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;
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace Symfony\Components\Finder\Comparator;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* DateCompare compiles date comparisons.
*
* @package Symfony
* @subpackage Components_Finder
* @author Fabien Potencier <fabien.potencier@symfony-project.com> 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);
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Symfony\Components\Finder;
namespace Symfony\Components\Finder\Comparator;
/*
* This file is part of the Symfony framework.
@ -33,71 +33,45 @@ namespace Symfony\Components\Finder;
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
* @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] : '==');
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\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')),
);
}
}

View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\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'))),
);
}
}

View File

@ -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');
}
}

View File

@ -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()

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\Finder\Iterator;
use Symfony\Components\Finder\Iterator\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')),
);
}
}

View File

@ -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);
}
}
}

View File

@ -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')),
);
}
}