浏览代码

[Finder] refactored size and date comparators

Fabien Potencier 15 年之前
父节点
当前提交
9426f820a7

+ 64 - 0
src/Symfony/Components/Finder/Comparator/Comparator.php

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

+ 57 - 0
src/Symfony/Components/Finder/Comparator/DateComparator.php

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

+ 14 - 40
src/Symfony/Components/Finder/NumberCompare.php

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

+ 15 - 36
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) {

+ 8 - 14
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;

+ 53 - 0
tests/Symfony/Tests/Components/Finder/Comparator/ComparatorTest.php

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

+ 63 - 0
tests/Symfony/Tests/Components/Finder/Comparator/DateComparatorTest.php

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

+ 8 - 8
tests/Symfony/Tests/Components/Finder/NumberCompareTest.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');
         }
     }

+ 8 - 1
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()

+ 41 - 0
tests/Symfony/Tests/Components/Finder/Iterator/DateRangeFilterIteratorTest.php

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

+ 18 - 8
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);
             }
         }
     }

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