Skip to content

Support php-cs-fixer #66

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

Merged
merged 15 commits into from
Jun 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
/vendor/
/build/
phpstan-phpqa.neon
.php_cs
.php_cs.cache
10 changes: 10 additions & 0 deletions .phpqa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)) |

Expand Down Expand Up @@ -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") |

Expand Down Expand Up @@ -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
Copy link

@keradus keradus May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may require risky support, as some of the rules require that to be enabled

[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/) |
Expand Down
126 changes: 126 additions & 0 deletions app/report/php-cs-fixer.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:output method="html" encoding="UTF-8"/>

<xsl:template match="/">
<html>
<head>
<title>PHP CS Fixer report</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<style>
.file {
background: #f9f9f9
}
.fixed-navbar {
list-style-type: none;
position: fixed;
top: 0;
right: 1em;
}
</style>
<script>
var onDocumentReady = [
function () {
$('[data-fixers]').each(function () {
var original = $(this).text();
var fixers = original.replace("applied fixers:\n---------------", '');
var html = fixers.split('* ').splice(1).join('<br />');
$(this).html(html);
});
}
];
</script>
</head>
<body>

<div class="container-fluid">

<h1>PHP CS Fixer report</h1>

<nav>
<ul class="nav nav-pills" role="tablist">
<li role="presentation" class="active">
<a href="#overview" aria-controls="overview" role="tab" data-toggle="tab">Overview</a>
</li>
<li role="presentation">
<a href="#errors" aria-controls="errors" role="tab" data-toggle="tab">Errors</a>
</li>
</ul>
</nav>

<div class="tab-content">

<div role="tabpanel" class="tab-pane active" id="overview">
<table class="table table-striped">
<thead>
<tr>
<th>Files</th>
<th>Errors</th>
<th>Duration</th>
</tr>
</thead>
<tr>
<td><xsl:value-of select="/testsuites/testsuite/@tests" /></td>
<th><span class="label label-danger"><xsl:value-of select="/testsuites/testsuite/@failures" /></span></th>
<th><span class="label label-info"><xsl:value-of select="/testsuites/testsuite/@time" /></span></th>
</tr>
</table>
</div>

<div role="tabpanel" class="tab-pane" id="errors">
<div class="fixed-navbar">
<div class="input-group" style="width: 20em">
<span class="input-group-addon"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></span>
<input data-search="errors" type="text" class="form-control" placeholder="trailing..." />
</div>
</div>
<script>
onDocumentReady.push(function () {
var rows = $('[data-filterable] tbody tr');

$("[data-search]").keyup(function () {
var term = $(this).val().toLowerCase();

rows.hide();
matchElements(rows).show();

function matchElements(elements) {
return elements.filter(function () {
var rowContent = $(this).text().toLowerCase();
return rowContent.indexOf(term) !== -1
});
}
});
});
</script>
<table class="table table-striped table-hover" data-filterable="errors">
<thead>
<tr>
<th>File</th>
<th>Errors (fixers)</th>
</tr>
</thead>
<xsl:for-each select="/testsuites/testsuite/testcase">
<xsl:for-each select="./failure">
<tr>
<td><strong><xsl:value-of select="../@name" /></strong></td>
<td data-fixers=""><xsl:value-of select="current()" /></td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</div>
</div>
</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script>
$(document).ready(onDocumentReady);
</script>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
4 changes: 4 additions & 0 deletions app/report/phpqa.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ set tabs = {
'overview': 'Overview',
'errors': 'Errors',
},
'php-cs-fixer': {
'overview': 'Overview',
'errors': 'Errors',
},
'phpmd': {
'overview': 'Overview',
'errors': 'Errors',
Expand Down
2 changes: 1 addition & 1 deletion bin/ci.sh
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions bin/suggested-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
54 changes: 51 additions & 3 deletions src/CodeAnalysisTasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -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',
),
);
Expand Down Expand Up @@ -384,13 +392,53 @@ function ($relativeDir) {
);
}

private function phpcsfixer()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you like, you could execute the code (FixCommand) instead of running it as separated CLI process

{
$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("<error>php-cs-fixer analyzes only first directory {$analyzedDir}</error>");
$this->say(
"- <info>multiple dirs are supported if you specify " .
"<comment>php-cs-fixer.config</comment> in <comment>.phpqa.yml</comment></info>"
);
}
}
$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(
Expand Down
10 changes: 10 additions & 0 deletions src/OutputMode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Edge\QA;

class OutputMode
{
const HANDLED_BY_TOOL = 0;
const RAW_CONSOLE_OUTPUT = 1;
const XML_CONSOLE_OUTPUT = 2;
}
13 changes: 9 additions & 4 deletions src/RunningTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class RunningTool
public $binary;
private $internalClass;
private $optionSeparator;
private $outputMode;

private $xmlFiles;
private $errorsXPath;
Expand All @@ -16,7 +17,6 @@ class RunningTool

public $htmlReport;
public $userReports = [];
public $hasOnlyConsoleOutput;
/** @var \Symfony\Component\Process\Process */
public $process;

Expand All @@ -28,7 +28,7 @@ public function __construct($tool, array $toolConfig)
'xml' => [],
'errorsXPath' => '',
'allowedErrorsCount' => null,
'hasOnlyConsoleOutput' => false,
'outputMode' => OutputMode::HANDLED_BY_TOOL,
'internalClass' => null,
];
$this->tool = $tool;
Expand All @@ -39,14 +39,19 @@ 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()
{
return !$this->internalClass || class_exists($this->internalClass);
}

public function hasOutput($outputMode)
{
return $this->outputMode == $outputMode;
}

public function buildOption($arg, $value)
{
if ($value || $value === 0) {
Expand All @@ -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, ''];
Expand Down
4 changes: 4 additions & 0 deletions tests/Config/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down