Browse Source

path argument is used to create an intersection with existing finder

Dariusz Ruminski 9 years ago
parent
commit
1570fd4f8a

+ 2 - 2
.travis.yml

@@ -12,7 +12,7 @@ matrix:
         - php: 5.5
         - php: 5.6
         - php: 7.0
-          env: SYMFONY_VERSION="~2.8"
+          env: SYMFONY_VERSION="^2.8"
 
 sudo: false
 
@@ -22,7 +22,7 @@ cache:
 
 before_install:
     - git config --global github.accesstoken 5e7538aa415005c606ea68de2bbbade0409b4b8c
-    - 'if [ "$SYMFONY_VERSION" != "" ]; then sed -i "s/\"symfony\/\([^\"]*\)\": \"[^\"]*\"/\"symfony\/\1\": \"$SYMFONY_VERSION\"/g" composer.json; fi'
+    - 'if [ "$SYMFONY_VERSION" != "" ]; then sed -i "s/\"symfony\/\([^\"]*\)\": \"^2[^\"]*\"/\"symfony\/\1\": \"$SYMFONY_VERSION\"/g" composer.json; fi'
 
 install:
     - travis_retry composer update $COMPOSER_FLAGS --no-interaction

+ 6 - 0
UPGRADE.md

@@ -34,6 +34,12 @@ CLI options
               | --rules       | Rules to be used            | option was added
               | --using-cache | Does cache should be used   | option was added
 
+CLI argument
+------------
+
+On 1.x line `path` argument allows you to specify the dir/file that will be fixed.
+On 2.x line `path` argument is a bit different. It is a mask for finder you are using (defined in your configuration file or default one). Only files pointed by both finder and CLI `path` argument will be fixed.
+
 Exit codes
 ----------
 

+ 2 - 1
composer.json

@@ -19,7 +19,8 @@
         "symfony/console": "^2.3 || ^3.0",
         "symfony/event-dispatcher": "^2.1 || ^3.0",
         "symfony/filesystem": "^2.1 || ^3.0",
-        "symfony/finder": "^2.1 || ^3.0",
+        "symfony/finder": "^2.2 || ^3.0",
+        "symfony/polyfill-php54": "^1.0",
         "symfony/process": "^2.3 || ^3.0",
         "symfony/stopwatch": "^2.5 || ^3.0",
         "sebastian/diff": "^1.1"

+ 54 - 5
src/Console/ConfigurationResolver.php

@@ -14,11 +14,14 @@ namespace PhpCsFixer\Console;
 
 use PhpCsFixer\ConfigInterface;
 use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
+use PhpCsFixer\Finder;
 use PhpCsFixer\FixerFactory;
 use PhpCsFixer\FixerInterface;
 use PhpCsFixer\RuleSet;
 use PhpCsFixer\StdinFileInfo;
 use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Finder\Finder as SymfonyFinder;
+use Symfony\Component\Finder\SplFileInfo as SymfonySplFileInfo;
 
 /**
  * The resolver that resolves configuration to use by command line options and config.
@@ -389,13 +392,59 @@ final class ConfigurationResolver
      */
     private function resolveConfigPath()
     {
-        if (is_file($this->path)) {
-            $this->config->finder(new \ArrayIterator(array(new \SplFileInfo($this->path))));
-        } elseif ($this->isStdIn) {
+        if ($this->isStdIn) {
             $this->config->finder(new \ArrayIterator(array(new StdinFileInfo())));
-        } elseif (null !== $this->path) {
-            $this->config->getFinder()->in($this->path);
+
+            return;
+        }
+
+        if (null === $this->path) {
+            return;
         }
+
+        // - when we already have a valid finder - create intersection iterator of current finder and provided path
+        // - when we don't have valid finder - prepare new iterator
+        $iterator = null;
+        $currentFinder = $this->config->getFinder();
+
+        try {
+            $currentIteratorItems = array_map(
+                function (\SplFileInfo $current) {
+                    return $current->getRealPath();
+                },
+                iterator_to_array($currentFinder, false)
+            );
+
+            $nestedIterator = is_file($this->path)
+                ? new \ArrayIterator(array(new SymfonySplFileInfo(
+                    $this->path,
+                    substr(dirname($this->path), strlen($this->cwd) + 1),
+                    substr($this->path, strlen($this->cwd) + 1)
+                )))
+                : Finder::create()->in($this->path)->getIterator()
+            ;
+
+            $iterator = new \CallbackFilterIterator(
+                $nestedIterator,
+                function (\SplFileInfo $current) use ($currentIteratorItems) {
+                    return in_array($current->getRealPath(), $currentIteratorItems, true);
+                }
+            );
+        } catch (\LogicException $e) {
+            if ($this->path) {
+                $iterator = new \ArrayIterator(array(new SymfonySplFileInfo(
+                    $this->path,
+                    substr(dirname($this->path), strlen($this->cwd) + 1),
+                    substr($this->path, strlen($this->cwd) + 1)
+                )));
+            } elseif ($currentFinder instanceof SymfonyFinder) {
+                $iterator = $currentFinder->in($this->path);
+            } else {
+                $iterator = Finder::create()->in($this->path);
+            }
+        }
+
+        $this->config->finder($iterator);
     }
 
     /**

+ 23 - 0
src/FileCacheManager.php

@@ -33,6 +33,7 @@ use Symfony\Component\Filesystem\Exception\IOException;
 final class FileCacheManager
 {
     private $cacheFile;
+    private $cacheFileRealDirName;
     private $isEnabled;
     private $rules;
     private $newHashes = array();
@@ -49,6 +50,7 @@ final class FileCacheManager
     {
         $this->isEnabled = $isEnabled;
         $this->cacheFile = $cacheFile;
+        $this->cacheFileRealDirName = dirname(realpath($cacheFile));
         $this->rules = $rules;
 
         $this->readFromFile();
@@ -65,6 +67,8 @@ final class FileCacheManager
             return true;
         }
 
+        $file = $this->getRelativePathname($file);
+
         if (!isset($this->oldHashes[$file])) {
             return true;
         }
@@ -85,6 +89,8 @@ final class FileCacheManager
             return;
         }
 
+        $file = $this->getRelativePathname($file);
+
         $this->newHashes[$file] = $this->calcHash($fileContent);
     }
 
@@ -133,6 +139,7 @@ final class FileCacheManager
         // Set hashes only if the cache is fresh, otherwise we need to parse all files
         if (!$this->isCacheStale($data['version'], $data['rules'])) {
             $this->oldHashes = $data['hashes'];
+            $this->newHashes = $this->oldHashes;
         }
     }
 
@@ -154,4 +161,20 @@ final class FileCacheManager
             throw new IOException(sprintf('Failed to write file "%s".', $this->cacheFile), 0, null, $this->cacheFile);
         }
     }
+
+    private function normalizePath($path)
+    {
+        return str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);
+    }
+
+    private function getRelativePathname($file)
+    {
+        $file = $this->normalizePath($file);
+
+        if (0 !== stripos($file, $this->cacheFileRealDirName.DIRECTORY_SEPARATOR)) {
+            return $file;
+        }
+
+        return substr($file, strlen($this->cacheFileRealDirName) + 1);
+    }
 }

+ 3 - 2
src/Fixer.php

@@ -142,11 +142,12 @@ class Fixer
     {
         $new = $old = file_get_contents($file->getRealPath());
 
+        $pathname = $file->getPathname();
         $name = $this->getFileRelativePathname($file);
 
         if (
             '' === $old
-            || !$fileCacheManager->needFixing($name, $old)
+            || !$fileCacheManager->needFixing($pathname, $old)
             // PHP 5.3 has a broken implementation of token_get_all when the file uses __halt_compiler() starting in 5.3.6
             || (PHP_VERSION_ID >= 50306 && PHP_VERSION_ID < 50400 && false !== stripos($old, '__halt_compiler()'))
         ) {
@@ -244,7 +245,7 @@ class Fixer
             }
         }
 
-        $fileCacheManager->setFile($name, $new);
+        $fileCacheManager->setFile($pathname, $new);
 
         $this->dispatchEvent(
             FixerFileProcessedEvent::NAME,

+ 156 - 2
tests/Console/ConfigurationResolverTest.php

@@ -14,8 +14,10 @@ namespace PhpCsFixer\Tests\Console;
 
 use PhpCsFixer\Config;
 use PhpCsFixer\Console\ConfigurationResolver;
+use PhpCsFixer\Finder;
 use PhpCsFixer\Fixer;
 use PhpCsFixer\Test\AccessibleObject;
+use Symfony\Component\Finder\SplFileInfo;
 
 /**
  * @author Katsuhiro Ogawa <ko.fivestar@gmail.com>
@@ -235,8 +237,7 @@ final class ConfigurationResolverTest extends \PHPUnit_Framework_TestCase
      */
     public function testResolveConfigFileChooseFileWithInvalidFile()
     {
-        $dirBase = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'ConfigurationResolverConfigFile'.DIRECTORY_SEPARATOR;
-        $dirBase = realpath($dirBase);
+        $dirBase = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'ConfigurationResolverConfigFile'.DIRECTORY_SEPARATOR);
         $this->resolver
             ->setOption('path', $dirBase.'/case_5')
             ->resolve();
@@ -250,6 +251,159 @@ final class ConfigurationResolverTest extends \PHPUnit_Framework_TestCase
             ->resolve();
 
         $this->assertSame(__DIR__.DIRECTORY_SEPARATOR.'Command', $this->resolver->getPath());
+
+        $this->resolver
+            ->setCwd(dirname(__DIR__))
+            ->setOption('path', basename(__DIR__))
+            ->resolve();
+
+        $this->assertSame(__DIR__, $this->resolver->getPath());
+    }
+
+    public function testResolvePathWithFileThatIsExcludedDirectly()
+    {
+        $this->config->getFinder()
+            ->in(__DIR__)
+            ->notPath(basename(__FILE__));
+
+        $this->resolver
+            ->setOption('path', __FILE__)
+            ->resolve();
+
+        $this->assertCount(0, $this->resolver->getConfig()->getFinder());
+    }
+
+    public function testResolvePathWithFileThatIsExcludedByDir()
+    {
+        $dir = dirname(__DIR__);
+        $this->config->getFinder()
+            ->in($dir)
+            ->exclude(basename(__DIR__));
+
+        $this->resolver
+            ->setOption('path', __FILE__)
+            ->resolve();
+
+        $this->assertCount(0, $this->resolver->getConfig()->getFinder());
+    }
+
+    public function testResolvePathWithFileThatIsNotExcluded()
+    {
+        $dir = __DIR__;
+        $this->config->getFinder()
+            ->in($dir)
+            ->notPath('foo-'.basename(__FILE__));
+
+        $this->resolver
+            ->setOption('path', __FILE__)
+            ->resolve();
+
+        $this->assertCount(1, $this->resolver->getConfig()->getFinder());
+    }
+
+    /**
+     * @dataProvider provideResolveIntersectionOfPathsCases
+     */
+    public function testResolveIntersectionOfPaths($expectedIntersectionItems, $configFinder, $option)
+    {
+        $this->config->finder($configFinder);
+        $this->resolver
+            ->setOption('path', $option)
+            ->resolve()
+        ;
+
+        $intersectionItems = array_map(
+            function (SplFileInfo $file) {
+                return $file->getRealPath();
+            },
+            iterator_to_array($this->resolver->getConfig()->getFinder(), false)
+        );
+
+        sort($expectedIntersectionItems);
+        sort($intersectionItems);
+
+        $this->assertSame($expectedIntersectionItems, $intersectionItems);
+    }
+
+    public function provideResolveIntersectionOfPathsCases()
+    {
+        $dir = __DIR__.'/../Fixtures/ConfigurationResolverPathsIntersection';
+        $cb = function (array $items) use ($dir) {
+            return array_map(
+                function ($item) use ($dir) {
+                    return realpath($dir.'/'.$item);
+                },
+                $items
+            );
+        };
+
+        return array(
+            // configured only by finder
+            array(
+                $cb(array('a1.php', 'a2.php', 'b/b1.php', 'b/b2.php', 'b_b/b_b1.php', 'c/c1.php', 'c/d/cd1.php')),
+                Finder::create()
+                    ->in($dir),
+                null,
+            ),
+            // configured only by argument
+            array(
+                $cb(array('a1.php', 'a2.php', 'b/b1.php', 'b/b2.php', 'b_b/b_b1.php', 'c/c1.php', 'c/d/cd1.php')),
+                Finder::create(),
+                $dir,
+            ),
+            // configured by finder, filtered by argument which is dir
+            array(
+                $cb(array('c/c1.php', 'c/d/cd1.php')),
+                Finder::create()
+                    ->in($dir),
+                $dir.'/c',
+            ),
+            // configured by finder, filtered by argument which is file
+            array(
+                $cb(array('c/c1.php')),
+                Finder::create()
+                    ->in($dir),
+                $dir.'/c/c1.php',
+            ),
+            // finder points to one dir while argument to another, not connected
+            array(
+                array(),
+                Finder::create()
+                    ->in($dir.'/b'),
+                $dir.'/c',
+            ),
+            // finder with excluded dir, argument point to excluded file
+            array(
+                array(),
+                Finder::create()
+                    ->in($dir)
+                    ->exclude('c'),
+                $dir.'/c/d/cd1.php',
+            ),
+            // finder with excluded dir, argument point to excluded parent
+            array(
+                $cb(array('c/c1.php')),
+                Finder::create()
+                    ->in($dir)
+                    ->exclude('c/d'),
+                $dir.'/c',
+            ),
+            // finder with excluded file, argument point to excluded parent
+            array(
+                $cb(array('c/d/cd1.php')),
+                Finder::create()
+                    ->in($dir)
+                    ->notPath('c/c1.php'),
+                $dir.'/c',
+            ),
+            // finder with excluded file, argument point to excluded parent
+            array(
+                $cb(array('b/b1.php', 'b/b2.php')),
+                Finder::create()
+                    ->in($dir),
+                $dir.'/b',
+            ),
+        );
     }
 
     public function testResolveIsDryRunViaStdIn()

+ 0 - 0
tests/Fixtures/ConfigurationResolverPathsIntersection/a1.php


+ 0 - 0
tests/Fixtures/ConfigurationResolverPathsIntersection/a2.php


+ 0 - 0
tests/Fixtures/ConfigurationResolverPathsIntersection/b/b1.php


Some files were not shown because too many files changed in this diff