diff --git a/.gitignore b/.gitignore
index 6730f31e..fcc2609d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
/vendor/
/build/
phpstan-phpqa.neon
+.php_cs
+.php_cs.cache
diff --git a/.phpqa.yml b/.phpqa.yml
index eee9c927..217432c0 100644
--- a/.phpqa.yml
+++ b/.phpqa.yml
@@ -12,6 +12,15 @@ phpcs:
# you can include custom reports (https://github.com/wikidi/codesniffer/blob/master/reports/wikidi/Summary.php#L39)
# ./vendor/owner/package/src/MySummaryReport.php: phpcs-summary.html
+php-cs-fixer:
+ # http://cs.sensiolabs.org/#usage
+ rules: '@PSR2'
+ allowRiskyRules: false
+ # by default the tool is runned in dry-run mode (no fixers are applied)
+ isDryRun: true
+ # alternatively you can define path to your .phpcs_file (rules/allowRiskyRules config is ignored)
+ config: null
+
phpmd:
standard: app/phpmd.xml
@@ -31,3 +40,4 @@ report:
phpcs: app/report/phpcs.xsl
pdepend: app/report/pdepend.xsl
phpmd: app/report/phpmd.xsl
+ php-cs-fixer: app/report/php-cs-fixer.xsl
diff --git a/README.md b/README.md
index 203d882c..315e0a1d 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@ Experimental tool is executed only if the tool is specified in `--tools`.
Tool | PHP | Supported since | Description | Status |
---- | --- | --------------- | ----------- | ------ |
+[php-cs-fixer](http://cs.sensiolabs.org/) | [`>= 5.3`](https://github.com/EdgedesignCZ/phpqa/pull/66#discussion_r115206573) | `1.12` | Automatically detect and fix PHP coding standards issues | stable |
[parallel-lint](https://github.com/JakubOnderka/PHP-Parallel-Lint) | `>= 5.4` | `1.9` | Check syntax of PHP files | stable |
[phpstan](https://github.com/phpstan/phpstan) | `>= 7.0` | `1.9` | Discover bugs in your code without running it | _experimental_ ([`v0.7`](https://github.com/EdgedesignCZ/phpqa/pull/43)) |
@@ -157,6 +158,7 @@ phpcs | [checkstyle.xml](https://edgedesigncz.github.io/phpqa/report/checkstyle.
pdepend | [pdepend-jdepend.xml](https://edgedesigncz.github.io/phpqa/report/pdepend-jdepend.xml), [pdepend-summary.xml](https://edgedesigncz.github.io/phpqa/report/pdepend-summary.xml), [pdepend-dependencies.xml](https://edgedesigncz.github.io/phpqa/report/pdepend-dependencies.xml), [pdepend-jdepend.svg](https://edgedesigncz.github.io/phpqa/report/pdepend-jdepend.svg), [pdepend-pyramid.svg](https://edgedesigncz.github.io/phpqa/report/pdepend-pyramid.svg) | ✗ |
phpmd | [phpmd.xml](https://edgedesigncz.github.io/phpqa/report/phpmd.xml) | [✓](https://github.com/phpmd/phpmd/blob/master/src/main/php/PHPMD/Renderer/TextRenderer.php#L47) |
phpmetrics | [phpmetrics.html (v1)](https://edgedesigncz.github.io/phpqa/report/phpmetrics.html), [phpmetrics/index.html (v2)](https://edgedesigncz.github.io/phpqa/report/phpmetrics/), [phpmetrics.xml](https://edgedesigncz.github.io/phpqa/report/phpmetrics.xml) | [✓](https://github.com/phpmetrics/PhpMetrics#usage) |
+php-cs-fixer | [php-cs-fixer.html](https://edgedesigncz.github.io/phpqa/report/php-cs-fixer.html) | [✓](http://cs.sensiolabs.org/#usage "txt output format") |
parallel-lint | [parallel-lint.html](https://edgedesigncz.github.io/phpqa/report/parallel-lint.html) | [✓](https://github.com/JakubOnderka/PHP-Parallel-Lint#example-output) |
phpstan | [phpstan.html](https://edgedesigncz.github.io/phpqa/report/phpstan.html), [phpstan-phpqa.neon](https://edgedesigncz.github.io/phpqa/report/phpstan-phpqa.neon) | [✓](https://edgedesigncz.github.io/phpqa/report/phpstan.html), [phpstan-phpqa.neon](https://edgedesigncz.github.io/phpqa/report/phpstan-phpqa.neon "Generated configuration is saved in current working directory") |
@@ -199,6 +201,10 @@ Tool | Settings | Default Value | Your value
[phpcs.standard](https://pear.php.net/manual/en/package.php.php-codesniffer.usage.php#package.php.php-codesniffer.usage.coding-standard) | Coding standard | PSR2 | Name of existing standard (`PEAR`, `PHPCS`, `PSR1`, `PSR2`, `Squiz`, `Zend`), or path to your coding standard
[phpcs.ignoreWarnings](https://github.com/EdgedesignCZ/phpqa/issues/53) | If number of allowed errors is compared with warnings+errors, or just errors from `checkstyle.xml` | `false` | Boolean value
[phpcs.reports](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Reporting) | Report types | [`full`](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Reporting#printing-full-and-summary-reports) report in [cli mode](#output-modes), [`checkstyle`](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Reporting#printing-a-checkstyle-report) in [file mode](#output-modes) | Predefined [report types](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Reporting) or [custom reports](https://github.com/wikidi/codesniffer#examples)
+[php-cs-fixer.rules](http://cs.sensiolabs.org/#usage) | Coding standard rules | `@PSR2` | String value
+[php-cs-fixer.allowRiskyRules](http://cs.sensiolabs.org/#usage) | Whether risky rules may run | `false` | Boolean value
+[php-cs-fixer.config](http://cs.sensiolabs.org/#usage) | Load configuration from [file](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/master/.php_cs.dist) | `null` | Path to `.phpcs` file
+[php-cs-fixer.isDryRun](http://cs.sensiolabs.org/#usage) | If code is just analyzed or fixers are applied | `true` | Boolean value
[phpmd](http://phpmd.org/documentation/creating-a-ruleset.html) | Ruleset | [Edgedesign's standard](/app/phpmd.xml) | Path to ruleset
[phpcpd](https://github.com/sebastianbergmann/phpcpd/blob/de9056615da6c1230f3294384055fa7d722c38fa/src/CLI/Command.php#L136) | Minimum number of lines/tokens for copy-paste detection | 5 lines, 70 tokens |
[phpstan](https://github.com/phpstan/phpstan#configuration) | Level, config file | Level 0, `%currentWorkingDirectory%/phpstan.neon` | Take a look at [phpqa config in tests/.travis](/tests/.travis/) |
diff --git a/app/report/php-cs-fixer.xsl b/app/report/php-cs-fixer.xsl
new file mode 100644
index 00000000..b8d21c1c
--- /dev/null
+++ b/app/report/php-cs-fixer.xsl
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+ PHP CS Fixer report
+
+
+
+
+
+
+
+
+
PHP CS Fixer report
+
+
+
+
+
+
+
+
+
+ | Files |
+ Errors |
+ Duration |
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+ | File |
+ Errors (fixers) |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/report/phpqa.html.twig b/app/report/phpqa.html.twig
index 01886efa..93f3ea3c 100644
--- a/app/report/phpqa.html.twig
+++ b/app/report/phpqa.html.twig
@@ -24,6 +24,10 @@ set tabs = {
'overview': 'Overview',
'errors': 'Errors',
},
+ 'php-cs-fixer': {
+ 'overview': 'Overview',
+ 'errors': 'Errors',
+ },
'phpmd': {
'overview': 'Overview',
'errors': 'Errors',
diff --git a/bin/ci.sh b/bin/ci.sh
index 7f81469c..20a36df9 100755
--- a/bin/ci.sh
+++ b/bin/ci.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-./phpqa --verbose --report --config tests/.travis --tools phpcs:0,phpmd:0,phpcpd:0,parallel-lint:0,phpstan,phpmetrics,phploc,pdepend
+./phpqa --verbose --report --config tests/.travis --tools phpcs:0,php-cs-fixer:0,phpmd:0,phpcpd:0,parallel-lint:0,phpstan,phpmetrics,phploc,pdepend
diff --git a/bin/suggested-tools.sh b/bin/suggested-tools.sh
index 4f35585e..9a40c65c 100755
--- a/bin/suggested-tools.sh
+++ b/bin/suggested-tools.sh
@@ -9,8 +9,8 @@ mode="$1"
if [ $mode = "install" ]
then
echo "Installing suggested tools"
- composer require jakub-onderka/php-parallel-lint jakub-onderka/php-console-highlighter phpstan/phpstan
+ composer require jakub-onderka/php-parallel-lint jakub-onderka/php-console-highlighter phpstan/phpstan friendsofphp/php-cs-fixer:~2.2
else
echo "Removing suggested tools"
- composer remove jakub-onderka/php-parallel-lint jakub-onderka/php-console-highlighter phpstan/phpstan
+ composer remove jakub-onderka/php-parallel-lint jakub-onderka/php-console-highlighter phpstan/phpstan friendsofphp/php-cs-fixer
fi
diff --git a/src/CodeAnalysisTasks.php b/src/CodeAnalysisTasks.php
index 13fe5d46..8b87e57b 100644
--- a/src/CodeAnalysisTasks.php
+++ b/src/CodeAnalysisTasks.php
@@ -41,6 +41,14 @@ trait CodeAnalysisTasks
'composer' => 'squizlabs/php_codesniffer',
'binary' => 'phpcs',
),
+ 'php-cs-fixer' => array(
+ 'optionSeparator' => ' ',
+ 'internalClass' => 'PhpCsFixer\Config',
+ 'outputMode' => OutputMode::XML_CONSOLE_OUTPUT,
+ 'composer' => 'friendsofphp/php-cs-fixer',
+ 'xml' => ['php-cs-fixer.xml'],
+ 'errorsXPath' => '//testsuites/testsuite/testcase/failure',
+ ),
'phpmd' => array(
'optionSeparator' => ' ',
'xml' => ['phpmd.xml'],
@@ -61,13 +69,13 @@ trait CodeAnalysisTasks
'parallel-lint' => array(
'optionSeparator' => ' ',
'internalClass' => 'JakubOnderka\PhpParallelLint\ParallelLint',
- 'hasOnlyConsoleOutput' => true,
+ 'outputMode' => OutputMode::RAW_CONSOLE_OUTPUT,
'composer' => 'jakub-onderka/php-parallel-lint',
),
'phpstan' => array(
'optionSeparator' => ' ',
'internalClass' => 'PHPStan\Analyser\Analyser',
- 'hasOnlyConsoleOutput' => true,
+ 'outputMode' => OutputMode::RAW_CONSOLE_OUTPUT,
'composer' => 'phpstan/phpstan',
),
);
@@ -384,13 +392,53 @@ function ($relativeDir) {
);
}
+ private function phpcsfixer()
+ {
+ $configFile = $this->config->value('php-cs-fixer.config');
+ if ($configFile) {
+ $analyzedDir = $this->options->getAnalyzedDirs(' ');
+ } else {
+ $analyzedDirs = $this->options->getAnalyzedDirs();
+ $analyzedDir = reset($analyzedDirs);
+ if (count($analyzedDirs) > 1) {
+ $this->say("php-cs-fixer analyzes only first directory {$analyzedDir}");
+ $this->say(
+ "- multiple dirs are supported if you specify " .
+ "php-cs-fixer.config in .phpqa.yml"
+ );
+ }
+ }
+ $args = [
+ 'fix',
+ $analyzedDir,
+ 'verbose' => '',
+ 'format' => $this->options->isSavedToFiles ? 'junit' : 'txt',
+ ];
+ if ($configFile) {
+ $args['config'] = $configFile;
+ } else {
+ $args += [
+ 'rules' => $this->config->value('php-cs-fixer.rules'),
+ 'allow-risky' => $this->config->value('php-cs-fixer.allowRiskyRules') ? 'yes' : 'no',
+ ];
+ }
+ if ($this->config->value('php-cs-fixer.isDryRun')) {
+ $args['dry-run'] = '';
+ }
+ return $args;
+ }
+
private function buildHtmlReport()
{
foreach ($this->usedTools as $tool) {
if (!$tool->htmlReport) {
$tool->htmlReport = $this->options->rawFile("{$tool->binary}.html");
}
- if ($tool->hasOnlyConsoleOutput) {
+ if ($tool->hasOutput(OutputMode::XML_CONSOLE_OUTPUT)) {
+ file_put_contents($this->options->rawFile("{$tool}.xml"), $tool->process->getOutput());
+ }
+
+ if ($tool->hasOutput(OutputMode::RAW_CONSOLE_OUTPUT)) {
twigToHtml(
'cli.html.twig',
array(
diff --git a/src/OutputMode.php b/src/OutputMode.php
new file mode 100644
index 00000000..d6a9e2db
--- /dev/null
+++ b/src/OutputMode.php
@@ -0,0 +1,10 @@
+ [],
'errorsXPath' => '',
'allowedErrorsCount' => null,
- 'hasOnlyConsoleOutput' => false,
+ 'outputMode' => OutputMode::HANDLED_BY_TOOL,
'internalClass' => null,
];
$this->tool = $tool;
@@ -39,7 +39,7 @@ public function __construct($tool, array $toolConfig)
$this->errorsXPath = is_array($config['errorsXPath'])
? $config['errorsXPath'] : [$this->errorsXPath => $config['errorsXPath']];
$this->allowedErrorsCount = $config['allowedErrorsCount'];
- $this->hasOnlyConsoleOutput = $config['hasOnlyConsoleOutput'];
+ $this->outputMode = $config['outputMode'];
}
public function isInstalled()
@@ -47,6 +47,11 @@ public function isInstalled()
return !$this->internalClass || class_exists($this->internalClass);
}
+ public function hasOutput($outputMode)
+ {
+ return $this->outputMode == $outputMode;
+ }
+
public function buildOption($arg, $value)
{
if ($value || $value === 0) {
@@ -65,7 +70,7 @@ public function analyzeResult($hasNoOutput = false)
{
$xpath = $this->errorsXPath[$this->errorsType];
- if ($hasNoOutput || $this->hasOnlyConsoleOutput) {
+ if ($hasNoOutput || $this->hasOutput(OutputMode::RAW_CONSOLE_OUTPUT)) {
return $this->evaluteErrorsCount($this->process->getExitCode() ? 1 : 0);
} elseif (!$xpath) {
return [true, ''];
diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php
index de3148a3..64a0c5a9 100644
--- a/tests/Config/ConfigTest.php
+++ b/tests/Config/ConfigTest.php
@@ -13,6 +13,10 @@ public function testLoadDefaultConfig()
assertThat($config->value('phpcs.ignoreWarnings'), identicalTo(false));
assertThat($config->value('phpcs.reports.cli'), is(nonEmptyArray()));
assertThat($config->value('phpcs.reports.file'), is(nonEmptyArray()));
+ assertThat($config->value('php-cs-fixer.rules'), is(nonEmptyString()));
+ assertThat($config->value('php-cs-fixer.isDryRun'), identicalTo(true));
+ assertThat($config->value('php-cs-fixer.allowRiskyRules'), identicalTo(false));
+ assertThat($config->path('php-cs-fixer.config'), is(nullValue()));
assertThat($config->path('phpmd.standard'), is(nonEmptyString()));
assertThat($config->value('phpstan.level'), identicalTo(0));
}