Browse Source

Enhance DescribeCommand, Fixer interfaces cleanup

Conflicts:
	src/Fixer/FixerInterface.php
SpacePossum 8 years ago
parent
commit
2d2c998dd7

+ 1 - 1
doc/COOKBOOK-FIXERS.md

@@ -87,7 +87,7 @@ final class RemoveCommentsFixer extends AbstractFixer
 ```
 
 Note how the class and file name match. Also keep in mind that all
-fixers must implement `FixerInterface`. In this case, the fixer is
+fixers must implement `Fixer\FixerInterface`. In this case, the fixer is
 inheriting from `AbstractFixer`, which fulfills the interface with some
 default behavior.
 

+ 10 - 16
src/AbstractFixer.php

@@ -13,14 +13,16 @@
 namespace PhpCsFixer;
 
 use PhpCsFixer\ConfigurationException\RequiredFixerConfigurationException;
-use PhpCsFixer\ConfigurationException\UnallowedFixerConfigurationException;
+use PhpCsFixer\Fixer\ConfigurableFixerInterface;
+use PhpCsFixer\Fixer\DescribedFixerInterface;
+use PhpCsFixer\Fixer\FixerInterface;
 
 /**
  * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  *
  * @internal
  */
-abstract class AbstractFixer implements FixerInterface
+abstract class AbstractFixer implements FixerInterface, DescribedFixerInterface
 {
     /**
      * @var WhitespacesFixerConfig
@@ -29,10 +31,12 @@ abstract class AbstractFixer implements FixerInterface
 
     public function __construct()
     {
-        try {
-            $this->configure(null);
-        } catch (RequiredFixerConfigurationException $e) {
-            // ignore
+        if ($this instanceof ConfigurableFixerInterface) {
+            try {
+                $this->configure(null);
+            } catch (RequiredFixerConfigurationException $e) {
+                // ignore
+            }
         }
 
         if ($this instanceof WhitespacesFixerConfigAwareInterface) {
@@ -40,16 +44,6 @@ abstract class AbstractFixer implements FixerInterface
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function configure(array $configuration = null)
-    {
-        if (null !== $configuration) {
-            throw new UnallowedFixerConfigurationException($this->getName(), 'Configuration is not allowed.');
-        }
-    }
-
     /**
      * {@inheritdoc}
      */

+ 1 - 1
src/AbstractPhpdocTypesFixer.php

@@ -121,7 +121,7 @@ abstract class AbstractPhpdocTypesFixer extends AbstractFixer
     }
 
     /**
-     * Prepair the type and normalize it.
+     * Prepare the type and normalize it.
      *
      * @param string $type
      *

+ 1 - 0
src/AbstractProxyFixer.php

@@ -12,6 +12,7 @@
 
 namespace PhpCsFixer;
 
+use PhpCsFixer\Fixer\FixerInterface;
 use PhpCsFixer\Tokenizer\Tokens;
 
 /**

+ 2 - 0
src/Config.php

@@ -12,6 +12,8 @@
 
 namespace PhpCsFixer;
 
+use PhpCsFixer\Fixer\FixerInterface;
+
 /**
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Katsuhiro Ogawa <ko.fivestar@gmail.com>

+ 2 - 0
src/ConfigInterface.php

@@ -12,6 +12,8 @@
 
 namespace PhpCsFixer;
 
+use PhpCsFixer\Fixer\FixerInterface;
+
 /**
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>

+ 155 - 45
src/Console/Command/DescribeCommand.php

@@ -12,11 +12,14 @@
 
 namespace PhpCsFixer\Console\Command;
 
-use PhpCsFixer\ConfigurationException\UnallowedFixerConfigurationException;
 use PhpCsFixer\Differ\DiffConsoleFormatter;
 use PhpCsFixer\Differ\SebastianBergmannDiffer;
+use PhpCsFixer\Fixer\ConfigurableFixerInterface;
+use PhpCsFixer\Fixer\DescribedFixerInterface;
+use PhpCsFixer\Fixer\FixerInterface;
+use PhpCsFixer\FixerDefinition;
+use PhpCsFixer\FixerDefinitionInterface;
 use PhpCsFixer\FixerFactory;
-use PhpCsFixer\FixerInterface;
 use PhpCsFixer\RuleSet;
 use PhpCsFixer\ShortFixerDefinition;
 use PhpCsFixer\StdinFileInfo;
@@ -28,11 +31,22 @@ use Symfony\Component\Console\Output\OutputInterface;
 
 /**
  * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ * @author SpacePossum
  *
  * @internal
  */
 final class DescribeCommand extends Command
 {
+    /**
+     * @var string[]
+     */
+    private $setNames;
+
+    /**
+     * @var array<string, FixerInterface>
+     */
+    private $fixers;
+
     /**
      * {@inheritdoc}
      */
@@ -55,31 +69,46 @@ final class DescribeCommand extends Command
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $name = $input->getArgument('name');
+        try {
+            if ('@' === $name[0]) {
+                $this->describeSet($output, $name);
 
-        if ('@' === substr($name, 0, 1)) {
-            $this->describeSet($input, $output, $name);
-        } else {
-            $this->describeRule($input, $output, $name);
+                return;
+            }
+
+            $this->describeRule($output, $name);
+        } catch (DescribeNameNotFoundException $e) {
+            $alternative = $this->getAlternative($e->getType(), $name);
+            $this->describeList($output, $e->getType());
+
+            throw new \InvalidArgumentException(sprintf(
+                '%s %s not found.%s',
+                ucfirst($e->getType()), $name, null === $alternative ? '' : ' Did you mean "'.$alternative.'"?'
+            ));
         }
     }
 
-    private function describeRule(InputInterface $input, OutputInterface $output, $name)
+    /**
+     * @param OutputInterface $output
+     * @param string          $name
+     */
+    private function describeRule(OutputInterface $output, $name)
     {
-        $fixerFactory = new FixerFactory();
-        $fixers = array();
-
-        foreach ($fixerFactory->registerBuiltInFixers()->getFixers() as $fixer) {
-            $fixers[$fixer->getName()] = $fixer;
-        }
+        $fixers = $this->getFixers();
 
         if (!isset($fixers[$name])) {
-            throw new \InvalidArgumentException(sprintf('Rule "%s" does not exist.', $name));
+            throw new DescribeNameNotFoundException($name, 'rule');
         }
 
+        /** @var FixerInterface $fixer */
         $fixer = $fixers[$name];
-        $definition = $fixer->getDefinition();
+        if ($fixer instanceof DescribedFixerInterface) {
+            $definition = $fixer->getDefinition();
+        } else {
+            $definition = new FixerDefinition('[n/a]', '[n/a]', array(), null);
+        }
 
-        $output->writeln(sprintf('<info>Description of %s rule.</info>', $name));
+        $output->writeln(sprintf('<info>Description of</info> %s <info>rule</info>.', $name));
         $output->writeln($definition->getSummary());
         if ($definition->getDescription()) {
             $output->writeln($definition->getDescription());
@@ -96,7 +125,7 @@ final class DescribeCommand extends Command
             $output->writeln('');
         }
 
-        if ($this->isFixerConfigurable($fixer)) {
+        if ($fixer instanceof ConfigurableFixerInterface) {
             $output->writeln('<comment>Fixer is configurable.</comment>');
 
             if ($definition->getConfigurationDescription()) {
@@ -123,7 +152,10 @@ final class DescribeCommand extends Command
             foreach ($definition->getCodeSamples() as $index => $codeSample) {
                 $old = $codeSample[0];
                 $tokens = Tokens::fromCode($old);
-                $fixer->configure($codeSample[1]);
+                if ($fixer instanceof ConfigurableFixerInterface) {
+                    $fixer->configure($codeSample[1]);
+                }
+
                 $fixer->fix(new StdinFileInfo(), $tokens);
                 $new = $tokens->generateCode();
                 $diff = $differ->diff($old, $new);
@@ -140,36 +172,40 @@ final class DescribeCommand extends Command
 
         if ($definition instanceof ShortFixerDefinition) {
             $output->writeln(sprintf('<question>This rule is not yet described, do you want to help us and describe it?</question>'));
-            $output->writeln('Contribute at <comment>https://github.com/FriendsOfPHP/PHP-CS-Fixer</comment>');
+            $output->writeln('Contribute at <comment>https://github.com/FriendsOfPHP/PHP-CS-Fixer</comment> !');
             $output->writeln('');
         }
     }
 
-    private function describeSet(InputInterface $input, OutputInterface $output, $name)
+    /**
+     * @param OutputInterface $output
+     * @param string          $name
+     */
+    private function describeSet(OutputInterface $output, $name)
     {
+        if (!in_array($name, $this->getSetNames(), true)) {
+            throw new DescribeNameNotFoundException($name, 'set');
+        }
+
         $ruleSet = new RuleSet(array($name => true));
         $rules = $ruleSet->getRules();
         ksort($rules);
 
-        $fixerFactory = new FixerFactory();
-        $fixers = array();
+        $fixers = $this->getFixers();
 
-        foreach ($fixerFactory->registerBuiltInFixers()->getFixers() as $fixer) {
-            $fixers[$fixer->getName()] = $fixer;
-        }
-
-        $output->writeln(sprintf('<info>Description of %s set.</info>', $name));
+        $output->writeln(sprintf('<info>Description of</info> %s <info>set.</info>', $name));
         $output->writeln('');
 
         $help = '';
 
-        $count = count($rules) - 1;
         foreach ($rules as $rule => $config) {
+            /** @var FixerDefinitionInterface $definition */
+            $definition = $fixers[$rule]->getDefinition();
             $help .= sprintf(
                 " * <info>%s</info>%s\n   | %s\n%s\n",
                 $rule,
-                $fixers[$rule]->isRisky() ? ' <error>[risky]</error>' : '',
-                $fixers[$rule]->getDescription(),
+                $fixers[$rule]->isRisky() ? ' <error>risky</error>' : '',
+                $definition->getSummary(),
                 true !== $config ? sprintf("   <comment>| Configuration: %s</comment>\n", $this->arrayToText($config)) : ''
             );
         }
@@ -178,23 +214,10 @@ final class DescribeCommand extends Command
     }
 
     /**
-     * @param FixerInterface $fixer
+     * @param array $data
      *
-     * @return bool
+     * @return string
      */
-    private function isFixerConfigurable(FixerInterface $fixer)
-    {
-        try {
-            $fixer->configure(array());
-
-            return true;
-        } catch (UnallowedFixerConfigurationException $e) {
-            return false;
-        } catch (\Exception $e) {
-            return true;
-        }
-    }
-
     private function arrayToText(array $data)
     {
         // Output modifications:
@@ -215,4 +238,91 @@ final class DescribeCommand extends Command
             var_export($data, true)
         );
     }
+
+    /**
+     * @return array<string, FixerInterface>
+     */
+    private function getFixers()
+    {
+        if (null !== $this->fixers) {
+            return $this->fixers;
+        }
+
+        $fixerFactory = new FixerFactory();
+        $fixers = array();
+
+        foreach ($fixerFactory->registerBuiltInFixers()->getFixers() as $fixer) {
+            $fixers[$fixer->getName()] = $fixer;
+        }
+
+        $this->fixers = $fixers;
+        ksort($this->fixers);
+
+        return $this->fixers;
+    }
+
+    /**
+     * @return string[]
+     */
+    private function getSetNames()
+    {
+        if (null !== $this->setNames) {
+            return $this->setNames;
+        }
+
+        $set = new RuleSet();
+        $this->setNames = $set->getSetDefinitionNames();
+        sort($this->setNames);
+
+        return $this->setNames;
+    }
+
+    /**
+     * @param string $type 'rule'|'set'
+     * @param string $name
+     *
+     * @return null|string
+     */
+    private function getAlternative($type, $name)
+    {
+        $other = null;
+        $alternatives = 'set' === $type ? $this->getSetNames() : array_keys($this->getFixers());
+
+        foreach ($alternatives as $alternative) {
+            $distance = levenshtein($name, $alternative);
+            if (3 > $distance) {
+                $other = $alternative;
+
+                break;
+            }
+        }
+
+        return $other;
+    }
+
+    /**
+     * @param OutputInterface $output
+     * @param string          $type   'rule'|'set'
+     */
+    private function describeList(OutputInterface $output, $type)
+    {
+        if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) {
+            $describe = array(
+                'set' => $this->getSetNames(),
+                'rules' => $this->getFixers(),
+            );
+        } elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
+            $describe = 'set' === $type ? array('set' => $this->getSetNames()) : array('rules' => $this->getFixers());
+        } else {
+            return;
+        }
+
+        /** @var string[] $items */
+        foreach ($describe as $list => $items) {
+            $output->writeln(sprintf('<comment>Defined %s:</comment>', $list));
+            foreach ($items as $name => $item) {
+                $output->writeln(sprintf('* <info>%s</info>', is_string($name) ? $name : $item));
+            }
+        }
+    }
 }

+ 57 - 0
src/Console/Command/DescribeNameNotFoundException.php

@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Console\Command;
+
+/**
+ * @author SpacePossum
+ *
+ * @internal
+ */
+final class DescribeNameNotFoundException extends \InvalidArgumentException
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var string 'rule'|'set'
+     */
+    private $type;
+
+    /**
+     * @param string $name
+     * @param string $type
+     */
+    public function __construct($name, $type)
+    {
+        $this->name = $name;
+        $this->type = $type;
+    }
+
+    /**
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+}

+ 6 - 278
src/Console/Command/FixCommand.php

@@ -14,16 +14,12 @@ namespace PhpCsFixer\Console\Command;
 
 use PhpCsFixer\Config;
 use PhpCsFixer\ConfigInterface;
-use PhpCsFixer\ConfigurationException\UnallowedFixerConfigurationException;
 use PhpCsFixer\Console\ConfigurationResolver;
 use PhpCsFixer\Console\Output\NullOutput;
 use PhpCsFixer\Console\Output\ProcessOutput;
 use PhpCsFixer\Error\Error;
 use PhpCsFixer\Error\ErrorsManager;
-use PhpCsFixer\FixerFactory;
-use PhpCsFixer\FixerInterface;
 use PhpCsFixer\Report\ReportSummary;
-use PhpCsFixer\RuleSet;
 use PhpCsFixer\Runner\Runner;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
@@ -54,28 +50,28 @@ final class FixCommand extends Command
      *
      * @var EventDispatcher
      */
-    protected $eventDispatcher;
+    private $eventDispatcher;
 
     /**
      * ErrorsManager instance.
      *
      * @var ErrorsManager
      */
-    protected $errorsManager;
+    private $errorsManager;
 
     /**
      * Stopwatch instance.
      *
      * @var Stopwatch
      */
-    protected $stopwatch;
+    private $stopwatch;
 
     /**
      * Config instance.
      *
      * @var ConfigInterface
      */
-    protected $defaultConfig;
+    private $defaultConfig;
 
     public function __construct()
     {
@@ -109,190 +105,8 @@ final class FixCommand extends Command
                 )
             )
             ->setDescription('Fixes a directory or a file.')
-            ->setHelp(<<<EOF
-The <info>%command.name%</info> command tries to fix as much coding standards
-problems as possible on a given file or files in a given directory and its subdirectories:
-
-    <info>$ php %command.full_name% /path/to/dir</info>
-    <info>$ php %command.full_name% /path/to/file</info>
-
-The <comment>--format</comment> option for the output format. Supported formats are ``txt`` (default one), ``json``, ``xml`` and ``junit``.
-
-NOTE: When using ``junit`` format report generates in accordance with JUnit xml schema from Jenkins (see docs/junit-10.xsd).
-
-The <comment>--verbose</comment> option will show the applied rules. When using the ``txt`` format it will also displays progress notifications.
-
-The <comment>--rules</comment> option limits the rules to apply on the
-project:
-
-    <info>$ php %command.full_name% /path/to/project --rules=@PSR2</info>
-
-By default, all PSR rules are run.
-
-The <comment>--rules</comment> option lets you choose the exact rules to
-apply (the rule names must be separated by a comma):
-
-    <info>$ php %command.full_name% /path/to/dir --rules=line_ending,full_opening_tag,indentation_type</info>
-
-You can also blacklist the rules you don't want by placing a dash in front of the rule name, if this is more convenient,
-using <comment>-name_of_fixer</comment>:
-
-    <info>$ php %command.full_name% /path/to/dir --rules=-full_opening_tag,-indentation_type</info>
-
-When using combinations of exact and blacklist rules, applying exact ruless along with above blacklisted results:
-
-    <info>$ php %command.full_name% /path/to/project --rules=@Symfony,-@PSR1,-blank_line_before_return,strict_comparison</info>
-
-A combination of <comment>--dry-run</comment> and <comment>--diff</comment> will
-display a summary of proposed fixes, leaving your files unchanged.
-
-The <comment>--allow-risky</comment> option allows you to set whether risky rules may run. Default value is taken from config file.
-Risky rule is a rule, which could change code behaviour. By default no risky rules are run.
-
-The command can also read from standard input, in which case it won't
-automatically fix anything:
-
-    <info>$ cat foo.php | php %command.full_name% --diff -</info>
-
-Choose from the list of available rules:
-
-{$this->getFixersHelp()}
-
-The <comment>--dry-run</comment> option displays the files that need to be
-fixed but without actually modifying them:
-
-    <info>$ php %command.full_name% /path/to/code --dry-run</info>
-
-Instead of using command line options to customize the rule, you can save the
-project configuration in a <comment>.php_cs.dist</comment> file in the root directory
-of your project. The file must return an instance of ``PhpCsFixer\ConfigInterface``,
-which lets you configure the rules, the files and directories that
-need to be analyzed. You may also create <comment>.php_cs</comment> file, which is
-the local configuration that will be used instead of the project configuration. It
-is a good practice to add that file into your <comment>.gitignore</comment> file.
-With the <comment>--config</comment> option you can specify the path to the
-<comment>.php_cs</comment> file.
-
-The example below will add two rules to the default list of PSR2 set rules:
-
-    <?php
-
-    \$finder = PhpCsFixer\Finder::create()
-        ->exclude('somedir')
-        ->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php')
-        ->in(__DIR__)
-    ;
-
-    return PhpCsFixer\Config::create()
-        ->setRules(array(
-            '@PSR2' => true,
-            'strict_param' => true,
-            'array_syntax' => array('syntax' => 'short'),
-        ))
-        ->setFinder(\$finder)
-    ;
-
-    ?>
-
-**NOTE**: ``exclude`` will work only for directories, so if you need to exclude file, try ``notPath``.
-
-See `Symfony\\\\Finder <http://symfony.com/doc/current/components/finder.html>`_
-online documentation for other `Finder` methods.
-
-You may also use a blacklist for the rules instead of the above shown whitelist approach.
-The following example shows how to use all ``Symfony`` rules but the ``full_opening_tag`` rule.
-
-    <?php
-
-    \$finder = PhpCsFixer\Finder::create()
-        ->exclude('somedir')
-        ->in(__DIR__)
-    ;
-
-    return PhpCsFixer\Config::create()
-        ->setRules(array(
-            '@Symfony' => true,
-            'full_opening_tag' => false,
-        ))
-        ->setFinder(\$finder)
-    ;
-
-    ?>
-
-You may want to use non-linux whitespaces in your project. Then you need to
-configure them in your config file. Please be aware that this feature is
-experimental.
-
-    <?php
-
-    return PhpCsFixer\Config::create()
-        ->setIndent("\\t")
-        ->setLineEnding("\\r\\n")
-    ;
-
-    ?>
-
-By using ``--using-cache`` option with yes or no you can set if the caching
-mechanism should be used.
-
-Caching
--------
-
-The caching mechanism is enabled by default. This will speed up further runs by
-fixing only files that were modified since the last run. The tool will fix all
-files if the tool version has changed or the list of rules has changed.
-Cache is supported only for tool downloaded as phar file or installed via
-composer.
-
-Cache can be disabled via ``--using-cache`` option or config file:
-
-    <?php
-
-    return PhpCsFixer\Config::create()
-        ->setUsingCache(false)
-    ;
-
-    ?>
-
-Cache file can be specified via ``--cache-file`` option or config file:
-
-    <?php
-
-    return PhpCsFixer\Config::create()
-        ->setCacheFile(__DIR__.'/.php_cs.cache')
-    ;
-
-    ?>
-
-Using PHP CS Fixer on CI
-------------------------
-
-Require ``friendsofphp/php-cs-fixer`` as a `dev`` dependency:
-
-    $ ./composer.phar require --dev friendsofphp/php-cs-fixer
-
-Then, add the following command to your CI:
-
-    $ vendor/bin/php-cs-fixer fix --config=.php_cs.dist -v --dry-run --using-cache=no --path-mode=intersection `git diff --name-only --diff-filter=ACMRTUXB \$COMMIT_RANGE`
-
-Where ``\$COMMIT_RANGE`` is your range of commits, eg ``\$TRAVIS_COMMIT_RANGE`` or ``HEAD~..HEAD``.
-
-Exit codes
-----------
-
-Exit code is build using following bit flags:
-
-*  0 OK.
-*  1 General error (or PHP/HHVM minimal requirement not matched).
-*  4 Some files have invalid syntax (only in dry-run mode).
-*  8 Some files need fixing (only in dry-run mode).
-* 16 Configuration error of the application.
-* 32 Configuration error of a Fixer.
-* 64 Exception raised within the application.
-
-(applies to exit codes of the `fix` command only)
-EOF
-            );
+            ->setHelp(FixCommandHelp::getHelpCopy())
+        ;
     }
 
     /**
@@ -410,74 +224,6 @@ EOF
         );
     }
 
-    protected function getFixersHelp()
-    {
-        $help = '';
-        $fixerFactory = new FixerFactory();
-        $fixers = $fixerFactory->registerBuiltInFixers()->getFixers();
-
-        // sort fixers by name
-        usort(
-            $fixers,
-            function (FixerInterface $a, FixerInterface $b) {
-                return strcmp($a->getName(), $b->getName());
-            }
-        );
-
-        $ruleSets = array();
-        foreach (RuleSet::create()->getSetDefinitionNames() as $setName) {
-            $ruleSets[$setName] = new RuleSet(array($setName => true));
-        }
-
-        $getSetsWithRule = function ($rule) use ($ruleSets) {
-            $sets = array();
-
-            foreach ($ruleSets as $setName => $ruleSet) {
-                if ($ruleSet->hasRule($rule)) {
-                    $sets[] = $setName;
-                }
-            }
-
-            return $sets;
-        };
-
-        $count = count($fixers) - 1;
-        foreach ($fixers as $i => $fixer) {
-            $sets = $getSetsWithRule($fixer->getName());
-
-            $description = $fixer->getDefinition()->getSummary();
-
-            $attributes = array();
-
-            if ($fixer->isRisky()) {
-                $attributes[] = 'risky';
-            }
-
-            if ($this->isFixerConfigurable($fixer)) {
-                $attributes[] = 'configurable';
-            }
-
-            $description = wordwrap($description, 72, "\n   | ");
-
-            if (!empty($sets)) {
-                $help .= sprintf(" * <comment>%s</comment> [%s]\n   | %s\n", $fixer->getName(), implode(', ', $sets), $description);
-            } else {
-                $help .= sprintf(" * <comment>%s</comment>\n   | %s\n", $fixer->getName(), $description);
-            }
-
-            if (count($attributes)) {
-                sort($attributes);
-                $help .= sprintf("   | *Rule is: %s.*\n", implode(', ', $attributes));
-            }
-
-            if ($count !== $i) {
-                $help .= "\n";
-            }
-        }
-
-        return $help;
-    }
-
     /**
      * @param bool $isDryRun
      * @param bool $hasChangedFiles
@@ -524,22 +270,4 @@ EOF
             $output->writeln(sprintf('%4d) %s', $i + 1, $error->getFilePath()));
         }
     }
-
-    /**
-     * @param FixerInterface $fixer
-     *
-     * @return bool
-     */
-    private function isFixerConfigurable(FixerInterface $fixer)
-    {
-        try {
-            $fixer->configure(array());
-
-            return true;
-        } catch (UnallowedFixerConfigurationException $e) {
-            return false;
-        } catch (\Exception $e) {
-            return true;
-        }
-    }
 }

+ 299 - 0
src/Console/Command/FixCommandHelp.php

@@ -0,0 +1,299 @@
+<?php
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Console\Command;
+
+use PhpCsFixer\Fixer\ConfigurableFixerInterface;
+use PhpCsFixer\Fixer\DescribedFixerInterface;
+use PhpCsFixer\Fixer\FixerInterface;
+use PhpCsFixer\FixerFactory;
+use PhpCsFixer\RuleSet;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ * @author SpacePossum
+ *
+ * @internal
+ */
+final class FixCommandHelp
+{
+    /**
+     * @return string
+     */
+    public static function getHelpCopy()
+    {
+        $template =
+            <<<EOF
+The <info>%command.name%</info> command tries to fix as much coding standards
+problems as possible on a given file or files in a given directory and its subdirectories:
+
+    <info>$ php %command.full_name% /path/to/dir</info>
+    <info>$ php %command.full_name% /path/to/file</info>
+
+The <comment>--format</comment> option for the output format. Supported formats are ``txt`` (default one), ``json``, ``xml`` and ``junit``.
+
+NOTE: When using ``junit`` format report generates in accordance with JUnit xml schema from Jenkins (see docs/junit-10.xsd).
+
+The <comment>--verbose</comment> option will show the applied rules. When using the ``txt`` format it will also displays progress notifications.
+
+The <comment>--rules</comment> option limits the rules to apply on the
+project:
+
+    <info>$ php %command.full_name% /path/to/project --rules=@PSR2</info>
+
+By default, all PSR rules are run.
+
+The <comment>--rules</comment> option lets you choose the exact rules to
+apply (the rule names must be separated by a comma):
+
+    <info>$ php %command.full_name% /path/to/dir --rules=line_ending,full_opening_tag,indentation_type</info>
+
+You can also blacklist the rules you don't want by placing a dash in front of the rule name, if this is more convenient,
+using <comment>-name_of_fixer</comment>:
+
+    <info>$ php %command.full_name% /path/to/dir --rules=-full_opening_tag,-indentation_type</info>
+
+When using combinations of exact and blacklist rules, applying exact ruless along with above blacklisted results:
+
+    <info>$ php %command.full_name% /path/to/project --rules=@Symfony,-@PSR1,-blank_line_before_return,strict_comparison</info>
+
+A combination of <comment>--dry-run</comment> and <comment>--diff</comment> will
+display a summary of proposed fixes, leaving your files unchanged.
+
+The <comment>--allow-risky</comment> option allows you to set whether risky rules may run. Default value is taken from config file.
+Risky rule is a rule, which could change code behaviour. By default no risky rules are run.
+
+The command can also read from standard input, in which case it won't
+automatically fix anything:
+
+    <info>$ cat foo.php | php %command.full_name% --diff -</info>
+
+Choose from the list of available rules:
+
+%%%FIXERS_DETAILS%%%
+
+The <comment>--dry-run</comment> option displays the files that need to be
+fixed but without actually modifying them:
+
+    <info>$ php %command.full_name% /path/to/code --dry-run</info>
+
+Instead of using command line options to customize the rule, you can save the
+project configuration in a <comment>.php_cs.dist</comment> file in the root directory
+of your project. The file must return an instance of ``PhpCsFixer\ConfigInterface``,
+which lets you configure the rules, the files and directories that
+need to be analyzed. You may also create <comment>.php_cs</comment> file, which is
+the local configuration that will be used instead of the project configuration. It
+is a good practice to add that file into your <comment>.gitignore</comment> file.
+With the <comment>--config</comment> option you can specify the path to the
+<comment>.php_cs</comment> file.
+
+The example below will add two rules to the default list of PSR2 set rules:
+
+    <?php
+
+    \$finder = PhpCsFixer\Finder::create()
+        ->exclude('somedir')
+        ->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php')
+        ->in(__DIR__)
+    ;
+
+    return PhpCsFixer\Config::create()
+        ->setRules(array(
+            '@PSR2' => true,
+            'strict_param' => true,
+            'array_syntax' => array('syntax' => 'short'),
+        ))
+        ->setFinder(\$finder)
+    ;
+
+    ?>
+
+**NOTE**: ``exclude`` will work only for directories, so if you need to exclude file, try ``notPath``.
+
+See `Symfony\\\\Finder <http://symfony.com/doc/current/components/finder.html>`_
+online documentation for other `Finder` methods.
+
+You may also use a blacklist for the rules instead of the above shown whitelist approach.
+The following example shows how to use all ``Symfony`` rules but the ``full_opening_tag`` rule.
+
+    <?php
+
+    \$finder = PhpCsFixer\Finder::create()
+        ->exclude('somedir')
+        ->in(__DIR__)
+    ;
+
+    return PhpCsFixer\Config::create()
+        ->setRules(array(
+            '@Symfony' => true,
+            'full_opening_tag' => false,
+        ))
+        ->setFinder(\$finder)
+    ;
+
+    ?>
+
+You may want to use non-linux whitespaces in your project. Then you need to
+configure them in your config file. Please be aware that this feature is
+experimental.
+
+    <?php
+
+    return PhpCsFixer\Config::create()
+        ->setIndent("\\t")
+        ->setLineEnding("\\r\\n")
+    ;
+
+    ?>
+
+By using ``--using-cache`` option with yes or no you can set if the caching
+mechanism should be used.
+
+Caching
+-------
+
+The caching mechanism is enabled by default. This will speed up further runs by
+fixing only files that were modified since the last run. The tool will fix all
+files if the tool version has changed or the list of rules has changed.
+Cache is supported only for tool downloaded as phar file or installed via
+composer.
+
+Cache can be disabled via ``--using-cache`` option or config file:
+
+    <?php
+
+    return PhpCsFixer\Config::create()
+        ->setUsingCache(false)
+    ;
+
+    ?>
+
+Cache file can be specified via ``--cache-file`` option or config file:
+
+    <?php
+
+    return PhpCsFixer\Config::create()
+        ->setCacheFile(__DIR__.'/.php_cs.cache')
+    ;
+
+    ?>
+
+Using PHP CS Fixer on CI
+------------------------
+
+Require ``friendsofphp/php-cs-fixer`` as a `dev`` dependency:
+
+    $ ./composer.phar require --dev friendsofphp/php-cs-fixer
+
+Then, add the following command to your CI:
+
+    $ vendor/bin/php-cs-fixer fix --config=.php_cs.dist -v --dry-run --using-cache=no --path-mode=intersection `git diff --name-only --diff-filter=ACMRTUXB \$COMMIT_RANGE`
+
+Where ``\$COMMIT_RANGE`` is your range of commits, eg ``\$TRAVIS_COMMIT_RANGE`` or ``HEAD~..HEAD``.
+
+Exit codes
+----------
+
+Exit code is build using following bit flags:
+
+*  0 OK.
+*  1 General error (or PHP/HHVM minimal requirement not matched).
+*  4 Some files have invalid syntax (only in dry-run mode).
+*  8 Some files need fixing (only in dry-run mode).
+* 16 Configuration error of the application.
+* 32 Configuration error of a Fixer.
+* 64 Exception raised within the application.
+
+(applies to exit codes of the `fix` command only)
+EOF
+        ;
+
+        return str_replace(
+           '%%%FIXERS_DETAILS%%%',
+            self::getFixersHelp(),
+            $template
+        );
+    }
+
+    private static function getFixersHelp()
+    {
+        $help = '';
+        $fixerFactory = new FixerFactory();
+        $fixers = $fixerFactory->registerBuiltInFixers()->getFixers();
+
+        // sort fixers by name
+        usort(
+            $fixers,
+            function (FixerInterface $a, FixerInterface $b) {
+                return strcmp($a->getName(), $b->getName());
+            }
+        );
+
+        $ruleSets = array();
+        foreach (RuleSet::create()->getSetDefinitionNames() as $setName) {
+            $ruleSets[$setName] = new RuleSet(array($setName => true));
+        }
+
+        $getSetsWithRule = function ($rule) use ($ruleSets) {
+            $sets = array();
+
+            foreach ($ruleSets as $setName => $ruleSet) {
+                if ($ruleSet->hasRule($rule)) {
+                    $sets[] = $setName;
+                }
+            }
+
+            return $sets;
+        };
+
+        $count = count($fixers) - 1;
+        foreach ($fixers as $i => $fixer) {
+            $sets = $getSetsWithRule($fixer->getName());
+
+            if ($fixer instanceof DescribedFixerInterface) {
+                $description = $fixer->getDefinition()->getSummary();
+            } else {
+                $description = '[n/a]';
+            }
+
+            $attributes = array();
+
+            if ($fixer->isRisky()) {
+                $attributes[] = 'risky';
+            }
+
+            if ($fixer instanceof ConfigurableFixerInterface) {
+                $attributes[] = 'configurable';
+            }
+
+            $description = wordwrap($description, 72, "\n   | ");
+
+            if (!empty($sets)) {
+                $help .= sprintf(" * <comment>%s</comment> [%s]\n   | %s\n", $fixer->getName(), implode(', ', $sets), $description);
+            } else {
+                $help .= sprintf(" * <comment>%s</comment>\n   | %s\n", $fixer->getName(), $description);
+            }
+
+            if (count($attributes)) {
+                sort($attributes);
+                $help .= sprintf("   | *Rule is: %s.*\n", implode(', ', $attributes));
+            }
+
+            if ($count !== $i) {
+                $help .= "\n";
+            }
+        }
+
+        return $help;
+    }
+}

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