Browse Source

Add DescribeCommand

Conflicts:
	src/Fixer/Alias/EchoToPrintFixer.php
	src/Fixer/Alias/PrintToEchoFixer.php
Dariusz Ruminski 8 years ago
parent
commit
63eabf4103

+ 7 - 0
src/AbstractFixer.php

@@ -90,6 +90,13 @@ abstract class AbstractFixer implements FixerInterface
         $this->whitespacesConfig = $config;
     }
 
+    public function getDefinition()
+    {
+        return new ShortFixerDefinition(
+            $this->getDescription()
+        );
+    }
+
     private function getDefaultWhitespacesFixerConfig()
     {
         static $defaultWhitespacesFixerConfig = null;

+ 2 - 0
src/Console/Application.php

@@ -12,6 +12,7 @@
 
 namespace PhpCsFixer\Console;
 
+use PhpCsFixer\Console\Command\DescribeCommand;
 use PhpCsFixer\Console\Command\FixCommand;
 use PhpCsFixer\Console\Command\ReadmeCommand;
 use PhpCsFixer\Console\Command\SelfUpdateCommand;
@@ -36,6 +37,7 @@ final class Application extends BaseApplication
 
         parent::__construct('PHP CS Fixer', self::VERSION);
 
+        $this->add(new DescribeCommand());
         $this->add(new FixCommand());
         $this->add(new ReadmeCommand());
         $this->add(new SelfUpdateCommand());

+ 218 - 0
src/Console/Command/DescribeCommand.php

@@ -0,0 +1,218 @@
+<?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\ConfigurationException\UnallowedFixerConfigurationException;
+use PhpCsFixer\Differ\DiffConsoleFormatter;
+use PhpCsFixer\Differ\SebastianBergmannDiffer;
+use PhpCsFixer\FixerFactory;
+use PhpCsFixer\FixerInterface;
+use PhpCsFixer\RuleSet;
+use PhpCsFixer\ShortFixerDefinition;
+use PhpCsFixer\StdinFileInfo;
+use PhpCsFixer\Tokenizer\Tokens;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * @internal
+ */
+final class DescribeCommand extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('describe')
+            ->setDefinition(
+                array(
+                    new InputArgument('name', InputArgument::REQUIRED, 'Name of rule / set.'),
+                )
+            )
+            ->setDescription('Describe rule / ruleset.')
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $name = $input->getArgument('name');
+
+        if ('@' === substr($name, 0, 1)) {
+            $this->describeSet($input, $output, $name);
+        } else {
+            $this->describeRule($input, $output, $name);
+        }
+    }
+
+    private function describeRule(InputInterface $input, OutputInterface $output, $name)
+    {
+        $fixerFactory = new FixerFactory();
+        $fixers = array();
+
+        foreach ($fixerFactory->registerBuiltInFixers()->getFixers() as $fixer) {
+            $fixers[$fixer->getName()] = $fixer;
+        }
+
+        if (!isset($fixers[$name])) {
+            throw new \InvalidArgumentException(sprintf('Rule "%s" does not exist.', $name));
+        }
+
+        $fixer = $fixers[$name];
+        $definition = $fixer->getDefinition();
+
+        $output->writeln(sprintf('<info>Description of %s rule.</info>', $name));
+        $output->writeln($definition->getSummary());
+        if ($definition->getDescription()) {
+            $output->writeln($definition->getDescription());
+        }
+        $output->writeln('');
+
+        if ($fixer->isRisky()) {
+            $output->writeln('<error>Fixer applying this rule is risky.</error>');
+
+            if ($definition->getRiskyDescription()) {
+                $output->writeln($definition->getRiskyDescription());
+            }
+
+            $output->writeln('');
+        }
+
+        if ($this->isFixerConfigurable($fixer)) {
+            $output->writeln('<comment>Fixer is configurable.</comment>');
+
+            if ($definition->getConfigurationDescription()) {
+                $output->writeln($definition->getConfigurationDescription());
+            }
+
+            if ($definition->getDefaultConfiguration()) {
+                $output->writeln(sprintf('Default configuration: <comment>%s</comment>.', $this->arrayToText($definition->getDefaultConfiguration())));
+            }
+
+            $output->writeln('');
+        }
+
+        if ($definition->getCodeSamples()) {
+            $output->writeln('Fixing examples:');
+
+            $differ = new SebastianBergmannDiffer();
+            $diffFormatter = new DiffConsoleFormatter($output->isDecorated(), sprintf(
+                '<comment>   ---------- begin diff ----------</comment>%s%%s%s<comment>   ----------- end diff -----------</comment>',
+                PHP_EOL,
+                PHP_EOL
+            ));
+
+            foreach ($definition->getCodeSamples() as $index => $codeSample) {
+                $old = $codeSample[0];
+                $tokens = Tokens::fromCode($old);
+                $fixer->configure($codeSample[1]);
+                $fixer->fix(new StdinFileInfo(), $tokens);
+                $new = $tokens->generateCode();
+                $diff = $differ->diff($old, $new);
+
+                if (null === $codeSample[1]) {
+                    $output->writeln(sprintf(' * Example #%d.', $index + 1));
+                } else {
+                    $output->writeln(sprintf(' * Example #%d. Fixing with configuration: <comment>%s</comment>.', $index + 1, $this->arrayToText($codeSample[1])));
+                }
+                $output->writeln($diffFormatter->format($diff, '   %s'));
+                $output->writeln('');
+            }
+        }
+
+        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('');
+        }
+    }
+
+    private function describeSet(InputInterface $input, OutputInterface $output, $name)
+    {
+        $ruleSet = new RuleSet(array($name => true));
+        $rules = $ruleSet->getRules();
+        ksort($rules);
+
+        $fixerFactory = new FixerFactory();
+        $fixers = array();
+
+        foreach ($fixerFactory->registerBuiltInFixers()->getFixers() as $fixer) {
+            $fixers[$fixer->getName()] = $fixer;
+        }
+
+        $output->writeln(sprintf('<info>Description of %s set.</info>', $name));
+        $output->writeln('');
+
+        $help = '';
+
+        $count = count($rules) - 1;
+        foreach ($rules as $rule => $config) {
+            $help .= sprintf(
+                " * <info>%s</info>%s\n   | %s\n%s\n",
+                $rule,
+                $fixers[$rule]->isRisky() ? ' <error>[risky]</error>' : '',
+                $fixers[$rule]->getDescription(),
+                true !== $config ? sprintf("   <comment>| Configuration: %s</comment>\n", $this->arrayToText($config)) : ''
+            );
+        }
+
+        $output->write($help);
+    }
+
+    /**
+     * @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;
+        }
+    }
+
+    private function arrayToText(array $data)
+    {
+        // Output modifications:
+        // - remove new-lines
+        // - combine multiple whitespaces
+        // - switch array-syntax to short array-syntax
+        // - remove whitespace at array opening
+        // - remove trailing array comma and whitespace at array closing
+        // - remove numeric array indexes
+        static $replaces = array(
+            array('#\r|\n#', '#\s{1,}#', '#array\s*\((.*)\)#s', '#\[\s+#', '#,\s*\]#', '#\d+\s*=>\s*#'),
+            array('', ' ', '[$1]', '[', ']', ''),
+        );
+
+        return preg_replace(
+            $replaces[0],
+            $replaces[1],
+            var_export($data, true)
+        );
+    }
+}

+ 1 - 1
src/Console/Command/FixCommand.php

@@ -445,7 +445,7 @@ EOF
         foreach ($fixers as $i => $fixer) {
             $sets = $getSetsWithRule($fixer->getName());
 
-            $description = $fixer->getDescription();
+            $description = $fixer->getDefinition()->getSummary();
 
             $attributes = array();
 

+ 1 - 1
src/Fixer/Alias/EregToPregFixer.php

@@ -117,7 +117,7 @@ final class EregToPregFixer extends AbstractFixer
     /**
      * {@inheritdoc}
      */
-    public function getDescription()
+    protected function getDescription()
     {
         return 'Replace deprecated ereg regular expression functions with preg.';
     }

+ 1 - 1
src/Fixer/Alias/MbStrFunctionsFixer.php

@@ -86,7 +86,7 @@ final class MbStrFunctionsFixer extends AbstractFunctionReferenceFixer
     /**
      * {@inheritdoc}
      */
-    public function getDescription()
+    protected function getDescription()
     {
         return 'Replace non multibyte-safe functions with corresponding mb function.';
     }

+ 1 - 1
src/Fixer/Alias/NoAliasFunctionsFixer.php

@@ -93,7 +93,7 @@ final class NoAliasFunctionsFixer extends AbstractFixer
     /**
      * {@inheritdoc}
      */
-    public function getDescription()
+    protected function getDescription()
     {
         return 'Master functions shall be used instead of aliases.';
     }

+ 5 - 5
src/Fixer/Alias/PowToExponentiationFixer.php

@@ -70,18 +70,18 @@ final class PowToExponentiationFixer extends AbstractFunctionReferenceFixer
     /**
      * {@inheritdoc}
      */
-    public function getDescription()
+    public function getPriority()
     {
-        return 'Converts \'pow()\' to \'**\'.';
+        // must be run before BinaryOperatorSpacesFixer
+        return 1;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getPriority()
+    protected function getDescription()
     {
-        // must be run before BinaryOperatorSpacesFixer
-        return 1;
+        return 'Converts \'pow()\' to \'**\'.';
     }
 
     /**

+ 1 - 1
src/Fixer/Alias/RandomApiMigrationFixer.php

@@ -103,7 +103,7 @@ final class RandomApiMigrationFixer extends AbstractFunctionReferenceFixer
     /**
      * {@inheritdoc}
      */
-    public function getDescription()
+    protected function getDescription()
     {
         return 'Replaces rand, srand, getrandmax functions calls with their mt_* analogs.';
     }

+ 8 - 8
src/Fixer/ArrayNotation/ArraySyntaxFixer.php

@@ -63,14 +63,6 @@ final class ArraySyntaxFixer extends AbstractFixer
         $this->resolveFixCallback();
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getDescription()
-    {
-        return 'PHP arrays should be declared using the configured syntax.';
-    }
-
     /**
      * {@inheritdoc}
      */
@@ -101,6 +93,14 @@ final class ArraySyntaxFixer extends AbstractFixer
         return $tokens->isTokenKindFound($this->candidateTokenKind);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    protected function getDescription()
+    {
+        return 'PHP arrays should be declared using the configured syntax.';
+    }
+
     /**
      * @param Tokens $tokens
      * @param int    $index

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