Skip to content

Commit 6485861

Browse files
[Feature] autofixer (#337)
* Autofix sniffs * Exclude Typehint declaration on custom sniff to avoid breaking compatibility * Autofix issues from php-cs-fixer * Set concrete configuration class in testCase * Add fix composer script * Clear command definition and add definition for fix command * Add display output for fix * Add fix command * Exclude weird rules with phpstan in ConfigResolver * Display fixed insights with PhpCsFixer * Update some suppressWarnings * Use errorOutput to display hint message * Add docs * Add tests to assert fix work as expected * Create a DefinitionBinder * Enable fix option for laravel * Always show recap of fixed issues * Change description for fix option Co-Authored-By: Oliver Nybroe <[email protected]> * Fix after merges * Increase .phpcs memory_limit override * Add total fixed in Json Formatter * Update src/Application/Adapters/Laravel/Commands/InsightsCommand.php Co-Authored-By: Oliver Nybroe <[email protected]> * Configuration : update function to know if fix is enabled * Change enableFix function in File * Create a trait FixPerFileCollector and use it in SniffDecorator and FixerDecorator * Fix fixture for test SniffDecorator Fix Co-authored-by: Oliver Nybroe <[email protected]>
1 parent 0b8c52a commit 6485861

35 files changed

+636
-81
lines changed

.phpcs.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<arg name="colors"/>
1515
<arg name="parallel" value="75"/>
1616
<arg value="p"/>
17-
<ini name="memory_limit" value="128M"/>
17+
<ini name="memory_limit" value="512M"/>
1818

1919
<!-- Configs -->
2020
<config name="installed_paths" value="vendor/slevomat/coding-standard"/>

composer.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@
8080
"@phpunit:test",
8181
"@insights"
8282
],
83+
"fix": [
84+
"@ecs:test --fix",
85+
"@insights --fix --quiet"
86+
],
8387
"post-install-cmd": [
8488
"@website:copy-changelog",
8589
"@website:copy-logo"
@@ -96,6 +100,7 @@
96100
"phpstan:test": "Run the phpstan tests.",
97101
"phpunit:test": "Run the phpunit tests.",
98102
"insights": "Run the phpinsights tests",
99-
"test": "Run all tests including phpstan, phpunit and ecs."
103+
"test": "Run all tests including phpstan, phpunit and ecs.",
104+
"fix": "Run ecs and phpinsights fixers."
100105
}
101106
}

config/routes/console.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
declare(strict_types=1);
44

55
use NunoMaduro\PhpInsights\Application\Console\Commands\AnalyseCommand;
6+
use NunoMaduro\PhpInsights\Application\Console\Commands\FixCommand;
67
use NunoMaduro\PhpInsights\Application\Console\Commands\InvokableCommand;
78
use NunoMaduro\PhpInsights\Application\Console\Definitions\AnalyseDefinition;
9+
use NunoMaduro\PhpInsights\Application\Console\Definitions\FixDefinition;
810

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

20+
$fixCommand = new InvokableCommand(
21+
'fix',
22+
$container->get(FixCommand::class),
23+
FixDefinition::get()
24+
);
25+
1826
return [
1927
$analyseCommand,
28+
$fixCommand,
2029
];
2130
})();

docs/get-started.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,25 @@ In laravel, launch command as usual with your path:
7676
php artisan insights path/to/analyse
7777
```
7878

79+
## Fixing errors automatically <Badge text="^2.0"/>
80+
81+
Some Insights support automatic fixing.
82+
To fix your code automatically, two way are possibles:
83+
84+
* Add `--fix` option to your command. The output will be the classical output, with a resume of all issues fixed.
85+
* Or launch `phpinsights fix [directory]`
86+
87+
```bash
88+
# Classical command with --fix option
89+
vendor/bin/phpinsights analyse path/to/analyse --fix
90+
91+
# In laravel
92+
php artisan insights path/to/analyse --fix
93+
94+
# Just fix
95+
vendor/bin/phpinsights fix path/to/analyse
96+
```
97+
7998
## Analyse multiple paths <Badge text="^2.0"/>
8099
You can ask `phpinsights` to analyse multiple directories, multiple files or even combining them by providing multiple paths with `analyse` command:
81100

phpinsights.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
declare(strict_types=1);
44

55
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineGlobalConstants;
6+
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenTraits;
67
use NunoMaduro\PhpInsights\Domain\Sniffs\ForbiddenSetterSniff;
78
use PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\EmptyStatementSniff;
89
use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff;
910
use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\NoSilencedErrorsSniff;
1011
use SlevomatCodingStandard\Sniffs\Functions\UnusedParameterSniff;
12+
use SlevomatCodingStandard\Sniffs\TypeHints\DisallowArrayTypeHintSyntaxSniff;
1113
use SlevomatCodingStandard\Sniffs\TypeHints\DisallowMixedTypeHintSniff;
1214

1315
return [
@@ -60,6 +62,12 @@
6062
'exclude' => [
6163
'src/Domain/Reflection.php',
6264
'src/Domain/Details.php',
65+
'src/Application/ConfigResolver.php',
66+
],
67+
],
68+
DisallowArrayTypeHintSyntaxSniff::class => [
69+
'exclude' => [
70+
'src/Application/ConfigResolver.php',
6371
],
6472
],
6573
ForbiddenSetterSniff::class => [
@@ -90,6 +98,11 @@
9098
'src/Domain/FileProcessors/FixerFileProcessor.php',
9199
],
92100
],
101+
ForbiddenTraits::class => [
102+
'exclude' => [
103+
'src/Domain/Insights/FixPerFileCollector.php',
104+
],
105+
],
93106
],
94107

95108
/*

phpstan.neon.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ parameters:
4242
path: src/Domain/Reflection.php
4343
- '#NunoMaduro\\PhpInsights\\Domain\\File::addFixableError#'
4444
- '#iterable type PhpCsFixer\\Tokenizer\\Tokens#'
45+
- '#Method NunoMaduro\\PhpInsights\\Application\\Console\\Definitions\\BaseDefinition::get\(\) is not final#'
4546
autoload_files:
4647
- %rootDir%/../../squizlabs/php_codesniffer/autoload.php
4748
reportUnmatchedIgnoredErrors: false

schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
"security issues": {
6868
"type": "integer",
6969
"minimum": 0
70+
},
71+
"fixed issues": {
72+
"type": "integer",
73+
"minimum": 0
7074
}
7175
}
7276
},

src/Application/Adapters/Laravel/Commands/InsightsCommand.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use NunoMaduro\PhpInsights\Domain\Container;
1313
use NunoMaduro\PhpInsights\Domain\Kernel;
1414
use NunoMaduro\PhpInsights\Domain\Reflection;
15+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
1516

1617
/**
1718
* @internal
@@ -36,6 +37,7 @@ public function handle(): int
3637
}
3738

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

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

5153
$output = (new Reflection($this->output))->get('output');
52-
return $analyseCommand->__invoke($this->input, $output);
54+
55+
$result = $analyseCommand->__invoke($this->input, $output);
56+
57+
if ($output instanceof ConsoleOutputInterface) {
58+
$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</>');
59+
}
60+
61+
return $result;
5362
}
5463

5564
public function configure(): void

src/Application/Console/Commands/AnalyseCommand.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ public function __invoke(InputInterface $input, OutputInterface $output): int
9797
}
9898

9999
$consoleStyle->newLine();
100-
$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>');
101100

102101
return (int) $hasError;
103102
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Application\Console\Commands;
6+
7+
use NunoMaduro\PhpInsights\Application\Console\Formatters\Console;
8+
use NunoMaduro\PhpInsights\Application\Console\OutputDecorator;
9+
use NunoMaduro\PhpInsights\Domain\Insights\InsightCollectionFactory;
10+
use NunoMaduro\PhpInsights\Domain\MetricsFinder;
11+
use Symfony\Component\Console\Input\InputInterface;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
14+
/**
15+
* @internal
16+
*/
17+
final class FixCommand
18+
{
19+
/**
20+
* @var \NunoMaduro\PhpInsights\Domain\Insights\InsightCollectionFactory
21+
*/
22+
private $collectionFactory;
23+
24+
public function __construct(
25+
InsightCollectionFactory $collectionFactory
26+
) {
27+
$this->collectionFactory = $collectionFactory;
28+
}
29+
30+
public function __invoke(InputInterface $input, OutputInterface $output): int
31+
{
32+
$metrics = MetricsFinder::find();
33+
$collection = $this->collectionFactory->get($metrics, $output);
34+
35+
$output = OutputDecorator::decorate($output);
36+
$formatter = new Console($input, $output);
37+
38+
$formatter->formatFix($collection, $metrics);
39+
40+
return 0;
41+
}
42+
}

src/Application/Console/Commands/InvokableCommand.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Symfony\Component\Console\Command\Command as BaseCommand;
88
use Symfony\Component\Console\Input\InputDefinition;
99
use Symfony\Component\Console\Input\InputInterface;
10+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
1011
use Symfony\Component\Console\Output\OutputInterface;
1112

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

3536
public function execute(InputInterface $input, OutputInterface $output): int
3637
{
37-
return call_user_func($this->callable, $input, $output);
38+
$result = call_user_func($this->callable, $input, $output);
39+
40+
if ($output instanceof ConsoleOutputInterface) {
41+
$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</>');
42+
}
43+
44+
return $result;
3845
}
3946
}

src/Application/Console/Definitions/AnalyseDefinition.php

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,18 @@
44

55
namespace NunoMaduro\PhpInsights\Application\Console\Definitions;
66

7-
use Symfony\Component\Console\Input\InputArgument;
87
use Symfony\Component\Console\Input\InputDefinition;
98
use Symfony\Component\Console\Input\InputOption;
109

1110
/**
1211
* @internal
1312
*/
14-
final class AnalyseDefinition
13+
final class AnalyseDefinition extends BaseDefinition
1514
{
1615
public static function get(): InputDefinition
1716
{
18-
return new InputDefinition([
19-
new InputArgument(
20-
'paths',
21-
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
22-
'Paths of directories or files to analyse'
23-
),
24-
new InputOption(
25-
'config-path',
26-
'c',
27-
InputOption::VALUE_OPTIONAL,
28-
'The configuration file path'
29-
),
17+
$definition = parent::get();
18+
$definition->addOptions([
3019
new InputOption(
3120
'min-quality',
3221
null,
@@ -74,6 +63,14 @@ public static function get(): InputDefinition
7463
InputOption::VALUE_OPTIONAL,
7564
'The composer file path'
7665
),
66+
new InputOption(
67+
'fix',
68+
null,
69+
InputOption::VALUE_NONE,
70+
'Enable auto-fix for fixable insights'
71+
),
7772
]);
73+
74+
return $definition;
7875
}
7976
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Application\Console\Definitions;
6+
7+
use Symfony\Component\Console\Input\InputArgument;
8+
use Symfony\Component\Console\Input\InputDefinition;
9+
use Symfony\Component\Console\Input\InputOption;
10+
11+
/**
12+
* Minimum definition for each of our commands.
13+
*
14+
* @internal
15+
*/
16+
abstract class BaseDefinition
17+
{
18+
public static function get(): InputDefinition
19+
{
20+
return new InputDefinition([
21+
new InputArgument(
22+
'paths',
23+
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
24+
'Paths of directories or files to analyse'
25+
),
26+
new InputOption(
27+
'config-path',
28+
'c',
29+
InputOption::VALUE_OPTIONAL,
30+
'The configuration file path'
31+
),
32+
]);
33+
}
34+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Application\Console\Definitions;
6+
7+
use Symfony\Component\Console\Application;
8+
use Symfony\Component\Console\Input\InputInterface;
9+
10+
/**
11+
* @internal
12+
*/
13+
final class DefinitionBinder
14+
{
15+
public static function bind(InputInterface $input): void
16+
{
17+
// merge application default definition with current command definition.
18+
$definition = (new Application())->getDefinition();
19+
20+
$commandDefinition = BaseDefinition::get();
21+
if ($input->getFirstArgument() !== 'fix') {
22+
$commandDefinition = AnalyseDefinition::get();
23+
}
24+
25+
$definition->addArguments($commandDefinition->getArguments());
26+
$definition->addOptions($commandDefinition->getOptions());
27+
28+
$input->bind($definition);
29+
}
30+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Application\Console\Definitions;
6+
7+
/**
8+
* @internal
9+
*/
10+
final class FixDefinition extends BaseDefinition
11+
{
12+
}

0 commit comments

Comments
 (0)