Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] autofixer #337

Merged
merged 29 commits into from Apr 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c6e479a
Autofix sniffs
Jibbarth Oct 26, 2019
3cafce5
Exclude Typehint declaration on custom sniff to avoid breaking compat…
Jibbarth Oct 26, 2019
cbbb3ac
Autofix issues from php-cs-fixer
Jibbarth Oct 26, 2019
d1355d7
Set concrete configuration class in testCase
Jibbarth Oct 26, 2019
80ac7e5
Add fix composer script
Jibbarth Oct 26, 2019
9a92dbb
Clear command definition and add definition for fix command
Jibbarth Dec 15, 2019
aefc243
Add display output for fix
Jibbarth Dec 15, 2019
58cf6bb
Add fix command
Jibbarth Dec 15, 2019
7745216
Exclude weird rules with phpstan in ConfigResolver
Jibbarth Dec 15, 2019
52a60b4
Display fixed insights with PhpCsFixer
Jibbarth Dec 15, 2019
02a31c7
Merge branch 'master' into feature/autofixer
Jibbarth Feb 9, 2020
93dbd8d
Update some suppressWarnings
Jibbarth Feb 9, 2020
a499471
Use errorOutput to display hint message
Jibbarth Feb 9, 2020
7da4c00
Add docs
Jibbarth Feb 9, 2020
1b62927
Add tests to assert fix work as expected
Jibbarth Feb 9, 2020
a198ab4
Create a DefinitionBinder
Jibbarth Feb 9, 2020
dc258bb
Enable fix option for laravel
Jibbarth Feb 9, 2020
a96e69b
Always show recap of fixed issues
Jibbarth Feb 9, 2020
d5c3f25
Change description for fix option
Jibbarth Feb 10, 2020
a261990
Merge branch 'master' into feature/autofixer
Jibbarth Mar 15, 2020
61bfa12
Fix after merges
Jibbarth Mar 15, 2020
b045ce4
Increase .phpcs memory_limit override
Jibbarth Mar 15, 2020
05da8e0
Add total fixed in Json Formatter
Jibbarth Mar 15, 2020
3b5091a
Update src/Application/Adapters/Laravel/Commands/InsightsCommand.php
Jibbarth Mar 18, 2020
f20fd19
Configuration : update function to know if fix is enabled
Jibbarth Mar 18, 2020
b38f576
Change enableFix function in File
Jibbarth Mar 18, 2020
d2ac381
Create a trait FixPerFileCollector and use it in SniffDecorator and F…
Jibbarth Mar 18, 2020
56bb87e
Fix fixture for test SniffDecorator Fix
Jibbarth Mar 18, 2020
73fcdfd
Merge branch 'master' into feature/autofixer
Jibbarth Apr 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .phpcs.xml.dist
Expand Up @@ -14,7 +14,7 @@
<arg name="colors"/>
<arg name="parallel" value="75"/>
<arg value="p"/>
<ini name="memory_limit" value="128M"/>
<ini name="memory_limit" value="512M"/>

<!-- Configs -->
<config name="installed_paths" value="vendor/slevomat/coding-standard"/>
Expand Down
7 changes: 6 additions & 1 deletion composer.json
Expand Up @@ -80,6 +80,10 @@
"@phpunit:test",
"@insights"
],
"fix": [
"@ecs:test --fix",
"@insights --fix --quiet"
],
"post-install-cmd": [
"@website:copy-changelog",
"@website:copy-logo"
Expand All @@ -96,6 +100,7 @@
"phpstan:test": "Run the phpstan tests.",
"phpunit:test": "Run the phpunit tests.",
"insights": "Run the phpinsights tests",
"test": "Run all tests including phpstan, phpunit and ecs."
"test": "Run all tests including phpstan, phpunit and ecs.",
"fix": "Run ecs and phpinsights fixers."
}
}
9 changes: 9 additions & 0 deletions config/routes/console.php
Expand Up @@ -3,8 +3,10 @@
declare(strict_types=1);

use NunoMaduro\PhpInsights\Application\Console\Commands\AnalyseCommand;
use NunoMaduro\PhpInsights\Application\Console\Commands\FixCommand;
use NunoMaduro\PhpInsights\Application\Console\Commands\InvokableCommand;
use NunoMaduro\PhpInsights\Application\Console\Definitions\AnalyseDefinition;
use NunoMaduro\PhpInsights\Application\Console\Definitions\FixDefinition;

return (static function (): array {
$container = require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'container.php';
Expand All @@ -15,7 +17,14 @@
AnalyseDefinition::get()
);

$fixCommand = new InvokableCommand(
'fix',
$container->get(FixCommand::class),
FixDefinition::get()
);

return [
$analyseCommand,
$fixCommand,
];
})();
19 changes: 19 additions & 0 deletions docs/get-started.md
Expand Up @@ -76,6 +76,25 @@ In laravel, launch command as usual with your path:
php artisan insights path/to/analyse
```

## Fixing errors automatically <Badge text="^2.0"/>

Some Insights support automatic fixing.
To fix your code automatically, two way are possibles:

* Add `--fix` option to your command. The output will be the classical output, with a resume of all issues fixed.
* Or launch `phpinsights fix [directory]`

```bash
# Classical command with --fix option
vendor/bin/phpinsights analyse path/to/analyse --fix

# In laravel
php artisan insights path/to/analyse --fix

# Just fix
vendor/bin/phpinsights fix path/to/analyse
```

## Analyse multiple paths <Badge text="^2.0"/>
You can ask `phpinsights` to analyse multiple directories, multiple files or even combining them by providing multiple paths with `analyse` command:

Expand Down
13 changes: 13 additions & 0 deletions phpinsights.php
Expand Up @@ -3,11 +3,13 @@
declare(strict_types=1);

use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineGlobalConstants;
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenTraits;
use NunoMaduro\PhpInsights\Domain\Sniffs\ForbiddenSetterSniff;
use PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\EmptyStatementSniff;
use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff;
use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\NoSilencedErrorsSniff;
use SlevomatCodingStandard\Sniffs\Functions\UnusedParameterSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\DisallowArrayTypeHintSyntaxSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\DisallowMixedTypeHintSniff;

return [
Expand Down Expand Up @@ -60,6 +62,12 @@
'exclude' => [
'src/Domain/Reflection.php',
'src/Domain/Details.php',
'src/Application/ConfigResolver.php',
],
],
DisallowArrayTypeHintSyntaxSniff::class => [
'exclude' => [
'src/Application/ConfigResolver.php',
],
],
ForbiddenSetterSniff::class => [
Expand Down Expand Up @@ -90,6 +98,11 @@
'src/Domain/FileProcessors/FixerFileProcessor.php',
],
],
ForbiddenTraits::class => [
'exclude' => [
'src/Domain/Insights/FixPerFileCollector.php',
],
],
],

/*
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Expand Up @@ -42,6 +42,7 @@ parameters:
path: src/Domain/Reflection.php
- '#NunoMaduro\\PhpInsights\\Domain\\File::addFixableError#'
- '#iterable type PhpCsFixer\\Tokenizer\\Tokens#'
- '#Method NunoMaduro\\PhpInsights\\Application\\Console\\Definitions\\BaseDefinition::get\(\) is not final#'
autoload_files:
- %rootDir%/../../squizlabs/php_codesniffer/autoload.php
reportUnmatchedIgnoredErrors: false
Expand Down
4 changes: 4 additions & 0 deletions schema.json
Expand Up @@ -67,6 +67,10 @@
"security issues": {
"type": "integer",
"minimum": 0
},
"fixed issues": {
"type": "integer",
"minimum": 0
}
}
},
Expand Down
11 changes: 10 additions & 1 deletion src/Application/Adapters/Laravel/Commands/InsightsCommand.php
Expand Up @@ -12,6 +12,7 @@
use NunoMaduro\PhpInsights\Domain\Container;
use NunoMaduro\PhpInsights\Domain\Kernel;
use NunoMaduro\PhpInsights\Domain\Reflection;
use Symfony\Component\Console\Output\ConsoleOutputInterface;

/**
* @internal
Expand All @@ -36,6 +37,7 @@ public function handle(): int
}

$configuration = require $configPath;
$configuration['fix'] = $this->hasOption('fix') && (bool) $this->option('fix') === true;
$configuration = ConfigResolver::resolve($configuration, $this->input);

$container = Container::make();
Expand All @@ -49,7 +51,14 @@ public function handle(): int
$analyseCommand = $container->get(AnalyseCommand::class);

$output = (new Reflection($this->output))->get('output');
return $analyseCommand->__invoke($this->input, $output);

$result = $analyseCommand->__invoke($this->input, $output);

if ($output instanceof ConsoleOutputInterface) {
$output->getErrorOutput()->writeln('✨ See something that needs to be improved? <options=bold>Create an issue</> or send us a <options=bold>pull request</>: <fg=cyan;options=bold>https://github.com/nunomaduro/phpinsights</>');
}
Jibbarth marked this conversation as resolved.
Show resolved Hide resolved

return $result;
}

public function configure(): void
Expand Down
1 change: 0 additions & 1 deletion src/Application/Console/Commands/AnalyseCommand.php
Expand Up @@ -97,7 +97,6 @@ public function __invoke(InputInterface $input, OutputInterface $output): int
}

$consoleStyle->newLine();
$consoleStyle->writeln('✨ See something that needs to be improved? <bold>Create an issue</bold> or send us a <bold>pull request</bold>: <title>https://github.com/nunomaduro/phpinsights</title>');

return (int) $hasError;
}
Expand Down
42 changes: 42 additions & 0 deletions src/Application/Console/Commands/FixCommand.php
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace NunoMaduro\PhpInsights\Application\Console\Commands;

use NunoMaduro\PhpInsights\Application\Console\Formatters\Console;
use NunoMaduro\PhpInsights\Application\Console\OutputDecorator;
use NunoMaduro\PhpInsights\Domain\Insights\InsightCollectionFactory;
use NunoMaduro\PhpInsights\Domain\MetricsFinder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @internal
*/
final class FixCommand
{
/**
* @var \NunoMaduro\PhpInsights\Domain\Insights\InsightCollectionFactory
*/
private $collectionFactory;

public function __construct(
InsightCollectionFactory $collectionFactory
) {
$this->collectionFactory = $collectionFactory;
}

public function __invoke(InputInterface $input, OutputInterface $output): int
{
$metrics = MetricsFinder::find();
$collection = $this->collectionFactory->get($metrics, $output);

$output = OutputDecorator::decorate($output);
$formatter = new Console($input, $output);

$formatter->formatFix($collection, $metrics);

return 0;
}
}
9 changes: 8 additions & 1 deletion src/Application/Console/Commands/InvokableCommand.php
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Console\Command\Command as BaseCommand;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class InvokableCommand extends BaseCommand
Expand Down Expand Up @@ -34,6 +35,12 @@ public function __construct(string $name, callable $callable, InputDefinition $d

public function execute(InputInterface $input, OutputInterface $output): int
{
return call_user_func($this->callable, $input, $output);
$result = call_user_func($this->callable, $input, $output);

if ($output instanceof ConsoleOutputInterface) {
$output->getErrorOutput()->writeln('✨ See something that needs to be improved? <options=bold>Create an issue</> or send us a <options=bold>pull request</>: <fg=cyan;options=bold>https://github.com/nunomaduro/phpinsights</>');
}

return $result;
}
}
25 changes: 11 additions & 14 deletions src/Application/Console/Definitions/AnalyseDefinition.php
Expand Up @@ -4,29 +4,18 @@

namespace NunoMaduro\PhpInsights\Application\Console\Definitions;

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

/**
* @internal
*/
final class AnalyseDefinition
final class AnalyseDefinition extends BaseDefinition
{
public static function get(): InputDefinition
{
return new InputDefinition([
new InputArgument(
'paths',
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
'Paths of directories or files to analyse'
),
new InputOption(
'config-path',
'c',
InputOption::VALUE_OPTIONAL,
'The configuration file path'
),
$definition = parent::get();
$definition->addOptions([
new InputOption(
'min-quality',
null,
Expand Down Expand Up @@ -74,6 +63,14 @@ public static function get(): InputDefinition
InputOption::VALUE_OPTIONAL,
'The composer file path'
),
new InputOption(
'fix',
null,
InputOption::VALUE_NONE,
'Enable auto-fix for fixable insights'
),
]);

return $definition;
}
}
34 changes: 34 additions & 0 deletions src/Application/Console/Definitions/BaseDefinition.php
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace NunoMaduro\PhpInsights\Application\Console\Definitions;

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

/**
* Minimum definition for each of our commands.
*
* @internal
*/
abstract class BaseDefinition
{
public static function get(): InputDefinition
{
return new InputDefinition([
new InputArgument(
'paths',
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
'Paths of directories or files to analyse'
),
new InputOption(
'config-path',
'c',
InputOption::VALUE_OPTIONAL,
'The configuration file path'
),
]);
}
}
30 changes: 30 additions & 0 deletions src/Application/Console/Definitions/DefinitionBinder.php
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace NunoMaduro\PhpInsights\Application\Console\Definitions;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;

/**
* @internal
*/
final class DefinitionBinder
Jibbarth marked this conversation as resolved.
Show resolved Hide resolved
{
public static function bind(InputInterface $input): void
{
// merge application default definition with current command definition.
$definition = (new Application())->getDefinition();

$commandDefinition = BaseDefinition::get();
if ($input->getFirstArgument() !== 'fix') {
$commandDefinition = AnalyseDefinition::get();
}

$definition->addArguments($commandDefinition->getArguments());
$definition->addOptions($commandDefinition->getOptions());

$input->bind($definition);
}
}
12 changes: 12 additions & 0 deletions src/Application/Console/Definitions/FixDefinition.php
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace NunoMaduro\PhpInsights\Application\Console\Definitions;

/**
* @internal
*/
final class FixDefinition extends BaseDefinition
{
}