Procházet zdrojové kódy

[Finder] refactored directory depth management

Fabien Potencier před 15 roky
rodič
revize
360f79f3a1

+ 31 - 1
src/Symfony/Components/Finder/Comparator/Comparator.php

@@ -21,13 +21,43 @@ namespace Symfony\Components\Finder\Comparator;
 class Comparator
 {
     protected $target;
-    protected $operator;
+    protected $operator = '==';
 
+    /**
+     * Gets the target value.
+     *
+     * @return string The target value
+     */
+    public function getTarget()
+    {
+        return $this->target;
+    }
+
+    /**
+     * Sets the target value.
+     *
+     * @param string $target The target value
+     */
     public function setTarget($target)
     {
         $this->target = $target;
     }
 
+    /**
+     * Gets the comparison operator.
+     *
+     * @return string The operator
+     */
+    public function getOperator()
+    {
+        return $this->operator;
+    }
+
+    /**
+     * Sets the comparison operator.
+     *
+     * @param string $operator A valid operator
+     */
     public function setOperator($operator)
     {
         if (!$operator) {

+ 1 - 1
src/Symfony/Components/Finder/Comparator/NumberComparator.php

@@ -12,7 +12,7 @@ namespace Symfony\Components\Finder\Comparator;
  */
 
 /**
- * NumberCompare compiles a simple comparison to an anonymous
+ * NumberComparator compiles a simple comparison to an anonymous
  * subroutine, which you can call with a value to be tested again.
  *
  * Now this would be very pointless, if NumberCompare didn't understand

+ 14 - 31
src/Symfony/Components/Finder/Finder.php

@@ -16,14 +16,12 @@ namespace Symfony\Components\Finder;
  *
  * It is a thin wrapper around several specialized iterator classes.
  *
- * All rules may be invoked several times, except for ->in() method.
- * Some rules are cumulative (->name() for example) whereas others are destructive
- * (most recent value is used, ->maxDepth() method for example).
+ * All rules may be invoked several times.
  *
  * All methods return the current Finder object to allow easy chaining:
  *
  * $finder = new Finder();
- * $iterator = $finder->files()->name('*.php')->in(__DIR__);
+ * $finder = $finder->files()->name('*.php')->in(__DIR__);
  *
  * @package    Symfony
  * @subpackage Components_Finder
@@ -36,8 +34,7 @@ class Finder implements \IteratorAggregate
     protected $notNames    = array();
     protected $exclude     = array();
     protected $filters     = array();
-    protected $minDepth    = 0;
-    protected $maxDepth    = INF;
+    protected $depths      = array();
     protected $sizes       = array();
     protected $followLinks = false;
     protected $sort        = false;
@@ -70,37 +67,23 @@ class Finder implements \IteratorAggregate
     }
 
     /**
-     * Sets the maximum directory depth.
+     * Adds tests for the directory depth.
      *
-     * The Finder will descend at most $level levels of directories below the starting point.
+     * Usage:
      *
-     * @param  int $level The max depth
+     *   $finder->depth('> 1') // the Finder will start matching at level 1.
+     *   $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
      *
-     * @return Symfony\Components\Finder The current Finder instance
-     *
-     * @see Symfony\Components\Finder\Iterator\LimitDepthFilterIterator
-     */
-    public function maxDepth($level)
-    {
-        $this->maxDepth = (double) $level;
-
-        return $this;
-    }
-
-    /**
-     * Sets the minimum directory depth.
-     *
-     * The Finder will start matching at level $level.
-     *
-     * @param  int $level The min depth
+     * @param  int $level The depth level expression
      *
      * @return Symfony\Components\Finder The current Finder instance
      *
-     * @see Symfony\Components\Finder\Iterator\LimitDepthFilterIterator
+     * @see Symfony\Components\Finder\Iterator\DepthRangeFilterIterator
+     * @see Symfony\Components\Finder\Comparator\NumberComparator
      */
-    public function minDepth($level)
+    public function depth($level)
     {
-        $this->minDepth = (integer) $level;
+        $this->depths[] = new Comparator\NumberComparator($level);
 
         return $this;
     }
@@ -365,8 +348,8 @@ class Finder implements \IteratorAggregate
 
         $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
 
-        if ($this->minDepth > 0 || $this->maxDepth < INF) {
-            $iterator = new Iterator\LimitDepthFilterIterator($iterator, $this->minDepth, $this->maxDepth);
+        if ($this->depths) {
+            $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->depths);
         }
 
         if ($this->mode) {

+ 6 - 6
src/Symfony/Components/Finder/Iterator/DateRangeFilterIterator.php

@@ -20,17 +20,17 @@ namespace Symfony\Components\Finder\Iterator;
  */
 class DateRangeFilterIterator extends \FilterIterator
 {
-    protected $patterns = array();
+    protected $comparators = array();
 
     /**
      * Constructor.
      *
-     * @param \Iterator $iterator The Iterator to filter
-     * @param array     $patterns An array of \DateCompare instances
+     * @param \Iterator $iterator    The Iterator to filter
+     * @param array     $comparators An array of \DateCompare instances
      */
-    public function __construct(\Iterator $iterator, array $patterns)
+    public function __construct(\Iterator $iterator, array $comparators)
     {
-        $this->patterns = $patterns;
+        $this->comparators = $comparators;
 
         parent::__construct($iterator);
     }
@@ -49,7 +49,7 @@ class DateRangeFilterIterator extends \FilterIterator
         }
 
         $filedate = $fileinfo->getMTime();
-        foreach ($this->patterns as $compare) {
+        foreach ($this->comparators as $compare) {
             if (!$compare->test($filedate)) {
                 return false;
             }

+ 69 - 0
src/Symfony/Components/Finder/Iterator/DepthRangeFilterIterator.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Symfony\Components\Finder\Iterator;
+
+/*
+ * 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.
+ */
+
+/**
+ * DepthRangeFilterIterator limits the directory depth.
+ *
+ * @package    Symfony
+ * @subpackage Components_Finder
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class DepthRangeFilterIterator extends \FilterIterator
+{
+    protected $minDepth = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param \Iterator $iterator    The Iterator to filter
+     * @param array     $comparators An array of \NumberComparator instances
+     */
+    public function __construct(\RecursiveIteratorIterator $iterator, array $comparators)
+    {
+        $minDepth = 0;
+        $maxDepth = INF;
+        foreach ($comparators as $comparator) {
+            switch ($comparator->getOperator()) {
+                case '>':
+                    $minDepth = $comparator->getTarget() + 1;
+                    break;
+                case '>=':
+                    $minDepth = $comparator->getTarget();
+                    break;
+                case '<':
+                    $maxDepth = $comparator->getTarget() - 1;
+                    break;
+                case '<=':
+                    $maxDepth = $comparator->getTarget();
+                    break;
+                default:
+                    $minDepth = $maxDepth = $comparator->getTarget();
+            }
+        }
+
+        $this->minDepth = $minDepth;
+        $iterator->setMaxDepth(INF === $maxDepth ? -1 : $maxDepth);
+
+        parent::__construct($iterator);
+    }
+
+    /**
+     * Filters the iterator values.
+     *
+     * @return Boolean true if the value should be kept, false otherwise
+     */
+    public function accept()
+    {
+        return $this->getInnerIterator()->getDepth() >= $this->minDepth;
+    }
+}

+ 0 - 50
src/Symfony/Components/Finder/Iterator/LimitDepthFilterIterator.php

@@ -1,50 +0,0 @@
-<?php
-
-namespace Symfony\Components\Finder\Iterator;
-
-/*
- * 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.
- */
-
-/**
- * LimitDepthFilterIterator limits the directory depth.
- *
- * @package    Symfony
- * @subpackage Components_Finder
- * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
- */
-class LimitDepthFilterIterator extends \FilterIterator
-{
-    protected $minDepth = 0;
-
-    /**
-     * Constructor.
-     *
-     * @param \Iterator $iterator The Iterator to filter
-     * @param integer   $minDepth The minimum depth
-     * @param integer   $maxDepth The maximum depth
-     */
-    public function __construct(\RecursiveIteratorIterator $iterator, $minDepth, $maxDepth)
-    {
-        $this->minDepth = (integer) $minDepth;
-
-        $iterator->setMaxDepth(INF === $maxDepth ? -1 : $maxDepth);
-
-        parent::__construct($iterator);
-    }
-
-    /**
-     * Filters the iterator values.
-     *
-     * @return Boolean true if the value should be kept, false otherwise
-     */
-    public function accept()
-    {
-        return $this->getInnerIterator()->getDepth() >= $this->minDepth;
-    }
-}

+ 6 - 6
src/Symfony/Components/Finder/Iterator/SizeRangeFilterIterator.php

@@ -20,17 +20,17 @@ namespace Symfony\Components\Finder\Iterator;
  */
 class SizeRangeFilterIterator extends \FilterIterator
 {
-    protected $patterns = array();
+    protected $comparators = array();
 
     /**
      * Constructor.
      *
-     * @param \Iterator $iterator The Iterator to filter
-     * @param array     $patterns An array of \NumberCompare instances
+     * @param \Iterator $iterator    The Iterator to filter
+     * @param array     $comparators An array of \NumberComparator instances
      */
-    public function __construct(\Iterator $iterator, array $patterns)
+    public function __construct(\Iterator $iterator, array $comparators)
     {
-        $this->patterns = $patterns;
+        $this->comparators = $comparators;
 
         parent::__construct($iterator);
     }
@@ -49,7 +49,7 @@ class SizeRangeFilterIterator extends \FilterIterator
         }
 
         $filesize = $fileinfo->getSize();
-        foreach ($this->patterns as $compare) {
+        foreach ($this->comparators as $compare) {
             if (!$compare->test($filesize)) {
                 return false;
             }

+ 12 - 1
tests/Symfony/Tests/Components/Finder/Comparator/ComparatorTest.php

@@ -15,7 +15,7 @@ use Symfony\Components\Finder\Comparator\Comparator;
 
 class ComparatorTest extends \PHPUnit_Framework_TestCase
 {
-    public function testSetOperator()
+    public function testGetSetOperator()
     {
         $comparator = new Comparator();
         try {
@@ -24,6 +24,17 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase
         } catch (\Exception $e) {
             $this->assertInstanceOf('InvalidArgumentException', $e, '->setOperator() throws an \InvalidArgumentException if the operator is not valid.');
         }
+
+        $comparator = new Comparator();
+        $comparator->setOperator('>');
+        $this->assertEquals('>', $comparator->getOperator(), '->getOperator() returns the current operator');
+    }
+
+    public function testGetSetTarget()
+    {
+        $comparator = new Comparator();
+        $comparator->setTarget(8);
+        $this->assertEquals(8, $comparator->getTarget(), '->getTarget() returns the target');
     }
 
     /**

+ 9 - 12
tests/Symfony/Tests/Components/Finder/FinderTest.php

@@ -52,25 +52,22 @@ class FinderTest extends Iterator\RealIteratorTestCase
         $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
     }
 
-    public function testMaxDepth()
+    public function testDepth()
     {
         $finder = new Finder();
-        $this->assertSame($finder, $finder->maxDepth(0));
+        $this->assertSame($finder, $finder->depth('< 1'));
         $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator());
-    }
 
-    public function testMinDepth()
-    {
         $finder = new Finder();
-        $this->assertSame($finder, $finder->minDepth(1));
+        $this->assertSame($finder, $finder->depth('<= 0'));
+        $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator());
+
+        $finder = new Finder();
+        $this->assertSame($finder, $finder->depth('>= 1'));
         $this->assertIterator($this->toAbsolute(array('foo/bar.tmp')), $finder->in(self::$tmpDir)->getIterator());
-    }
 
-    public function testMinMaxDepth()
-    {
         $finder = new Finder();
-        $finder->maxDepth(0);
-        $finder->minDepth(1);
+        $finder->depth('< 1')->depth('>= 1');
         $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
     }
 
@@ -187,7 +184,7 @@ class FinderTest extends Iterator\RealIteratorTestCase
         }
 
         $finder = new Finder();
-        $iterator = $finder->files()->name('*.php')->maxDepth(0)->in(array(self::$tmpDir, __DIR__))->getIterator();
+        $iterator = $finder->files()->name('*.php')->depth('< 1')->in(array(self::$tmpDir, __DIR__))->getIterator();
 
         $this->assertIterator(array(self::$tmpDir.'test.php', __DIR__.'/FinderTest.php', __DIR__.'/GlobTest.php'), $iterator);
     }

+ 46 - 0
tests/Symfony/Tests/Components/Finder/Iterator/DepthRangeIteratorTest.php

@@ -0,0 +1,46 @@
+<?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\DepthRangeFilterIterator;
+use Symfony\Components\Finder\Comparator\NumberComparator;
+
+require_once __DIR__.'/RealIteratorTestCase.php';
+
+class DepthRangeFilterIteratorTest extends RealIteratorTestCase
+{
+    /**
+     * @dataProvider getAcceptData
+     */
+    public function testAccept($size, $expected)
+    {
+        $inner = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(sys_get_temp_dir().'/symfony2_finder', \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
+
+        $iterator = new DepthRangeFilterIterator($inner, $size);
+
+        $actual = array_keys(iterator_to_array($iterator));
+        sort($expected);
+        sort($actual);
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function getAcceptData()
+    {
+        return array(
+            array(array(new NumberComparator('< 1')), 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/test.php', sys_get_temp_dir().'/symfony2_finder/toto')),
+            array(array(new NumberComparator('<= 1')), 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 NumberComparator('> 1')), array()),
+            array(array(new NumberComparator('>= 1')), array(sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp')),
+            array(array(new NumberComparator('1')), array(sys_get_temp_dir().'/symfony2_finder/foo/bar.tmp')),
+        );
+    }
+}