Skip to content

Commit 9e577d2

Browse files
committed
Bump to codechecker/codenisffer 3.0
Changes in commands are important because all the old CLI machinery in phpcs 3.0 has been removed. There were some different solutions for this: - start using the bin/phpcs binary (exec) and forget. - create a custom runner/reporter like we did in local_codechecker. - use exclusively the new phpcs 3.0 classes to get it done. While I was really tempted to re-use the local_codechecker custom runner / reporter, finally I went the pure phpcs classes way to avoid creating any dependency on local_codechecker (apart from the standard). Unit tests have been slightly improved to better assert that all the expected elements in the output are there. Finally, we have had to instruct phpcs where the PHPCompatibility standard is because, when running from PHAR, the CodeSniffer.conf file bundled is not read and it's expected to be in real filesystem. This was causing codechecher not to work from PHAR, so we are now configuring it from the commands (Checker and Fixer). Closes #49
1 parent 5ad54af commit 9e577d2

File tree

9 files changed

+250
-107
lines changed

9 files changed

+250
-107
lines changed

composer.json

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,11 @@
3434
"type": "package",
3535
"package": {
3636
"name": "moodlehq/moodle-local_codechecker",
37-
"version": "2.9.8",
37+
"version": "3.0.0",
3838
"source": {
3939
"url": "https://github.com/moodlehq/moodle-local_codechecker.git",
4040
"type": "git",
41-
"reference": "v2.9.8"
42-
},
43-
"autoload": {
44-
"classmap": [
45-
"pear/PHP/CodeSniffer.php",
46-
"pear/PHP/CodeSniffer/CLI.php"
47-
]
41+
"reference": "v3.0.0"
4842
}
4943
}
5044
},
@@ -75,7 +69,7 @@
7569
],
7670
"require": {
7771
"php": ">=7.0.8",
78-
"moodlehq/moodle-local_codechecker": "^2.9.8",
72+
"moodlehq/moodle-local_codechecker": "^3.0.0",
7973
"moodlehq/moodle-local_ci": "^1.0.8",
8074
"moodlehq/moodle-local_moodlecheck": "^1.0.4",
8175
"sebastian/phpcpd": "^3.0",

composer.lock

Lines changed: 4 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The format of this change log follows the advice given at [Keep a CHANGELOG](htt
1818
- `moodle-plugin-ci phpunit` when coverage report is included, phpdbg is called with ignore memory limits param
1919
to avoid memory exhaused errors.
2020
- Updated project dependencies to current [moodle-local_moodlecheck](https://github.com/moodlehq/moodle-local_moodlecheck) and [moodle-local_ci](https://github.com/moodlehq/moodle-local_ci) versions.
21+
- Updated version of [moodle-local_codechecker](https://github.com/moodlehq/moodle-local_codechecker) to v3.0.0.
2122

2223
### Added
2324
- Detect existence of legacy php-webdriver, and use a different Firefox image when it is in use

psalm.xml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,6 @@
8787
</errorLevel>
8888
</MissingReturnType>
8989

90-
<!-- CodeSniffer has some bad PHPDoc -->
91-
<InvalidPropertyAssignmentValue>
92-
<errorLevel type="suppress">
93-
<file name="src/Bridge/CodeSnifferCLI.php"/>
94-
</errorLevel>
95-
</InvalidPropertyAssignmentValue>
96-
9790
<!-- CodeSniffer has some bad PHPDoc -->
9891
<NullArgument>
9992
<errorLevel type="suppress">
@@ -102,10 +95,12 @@
10295
</errorLevel>
10396
</NullArgument>
10497

105-
<!-- It is defined in Moodle itself -->
98+
<!-- It is defined in Moodle itself or included by the commands -->
10699
<UndefinedClass>
107100
<errorLevel type="suppress">
108101
<file name="src/Bridge/Moodle.php"/>
102+
<file name="src/Command/CodeCheckerCommand.php"/>
103+
<file name="src/Command/CodeFixerCommand.php"/>
109104
</errorLevel>
110105
</UndefinedClass>
111106
</issueHandlers>

src/Bridge/CodeSnifferCLI.php

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/Command/CodeCheckerCommand.php

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,24 @@
1212

1313
namespace MoodlePluginCI\Command;
1414

15-
use MoodlePluginCI\Bridge\CodeSnifferCLI;
1615
use MoodlePluginCI\StandardResolver;
16+
use PHP_CodeSniffer\Config;
17+
use PHP_CodeSniffer\Reporter;
18+
use PHP_CodeSniffer\Runner;
19+
use PHP_CodeSniffer\Util\Timing;
1720
use Symfony\Component\Console\Input\InputInterface;
1821
use Symfony\Component\Console\Input\InputOption;
1922
use Symfony\Component\Console\Output\OutputInterface;
2023
use Symfony\Component\Finder\Finder;
2124

25+
// Cannot be autoload from composer, because this autoloader is used for both
26+
// phpcs and any standard Sniff, so it must be loaded at the end. For more info, see:
27+
// https://github.com/squizlabs/PHP_CodeSniffer/issues/1463#issuecomment-300637855
28+
//
29+
// The alternative is to, instead of using PHP_CodeSniffer like a library, just
30+
// use the binaries bundled with it (phpcs, phpcbf...) but we want to use as lib for now.
31+
require_once __DIR__.'/../../vendor/moodlehq/moodle-local_codechecker/phpcs/autoload.php';
32+
2233
/**
2334
* Run Moodle Code Checker on a plugin.
2435
*/
@@ -72,24 +83,86 @@ protected function execute(InputInterface $input, OutputInterface $output)
7283
return $this->outputSkip($output);
7384
}
7485

75-
// Must define this before the sniffer due to odd code inclusion resulting in sniffer being included twice.
76-
$cli = new CodeSnifferCLI([
77-
'reports' => ['full' => null],
78-
'colors' => $output->isDecorated(),
79-
'encoding' => 'utf-8',
80-
'showProgress' => true,
81-
'reportWidth' => 120,
82-
]);
86+
// Needed constant.
87+
if (defined('PHP_CODESNIFFER_CBF') === false) {
88+
define('PHP_CODESNIFFER_CBF', false);
89+
}
8390

84-
\PHP_CodeSniffer::setConfigData('testVersion', PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION, true);
91+
Timing::startTiming();
92+
93+
$runner = new Runner();
94+
$runner->config = new Config(['--parallel=1']); // Pass a param to shortcut params coming from caller CLI.
95+
96+
// This is not needed normally, because phpcs loads the CodeSniffer.conf from its
97+
// root directory. But when this is run from within a .phar file, it expects the
98+
// config file to be out from the phar, in the same directory.
99+
//
100+
// While this approach is logic and enabled to configure phpcs, we don't want that
101+
// in this case, we just want to ensure phpcs knows about our standards,
102+
// so we are going to force the installed_paths config here.
103+
//
104+
// And it needs need to do it BEFORE the runner init! (or it's lost)
105+
//
106+
// Note: the "moodle" one is not really needed, because it's autodetected normally,
107+
// but the PHPCompatibility one is. There are some issues about version PHPCompatibility 10
108+
// to stop requiring to be configured here, but that's future version. Revisit this when
109+
// available.
110+
//
111+
// Note: the paths are relative to the base CodeSniffer directory, aka, the directory
112+
// where "src" sits.
113+
$runner->config->setConfigData('installed_paths', './../PHPCompatibility');
114+
$runner->config->standards = [$this->standard]; // Also BEFORE init() or it's lost.
115+
116+
$runner->init();
117+
118+
$runner->config->parallel = 1;
119+
$runner->config->reports = ['full' => null];
120+
$runner->config->colors = $output->isDecorated();
121+
$runner->config->verbosity = 0;
122+
$runner->config->encoding = 'utf-8';
123+
$runner->config->showProgress = true;
124+
$runner->config->showSources = true;
125+
$runner->config->interactive = false;
126+
$runner->config->cache = false;
127+
$runner->config->extensions = ['php' => 'PHP'];
128+
$runner->config->reportWidth = 132;
129+
130+
$runner->config->files = $files;
131+
132+
$runner->config->setConfigData('testVersion', PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION, true);
133+
134+
// Create the reporter to manage all the reports from the run.
135+
$runner->reporter = new Reporter($runner->config);
136+
137+
// And build the file list to iterate over.
138+
/** @var object[] $todo */
139+
$todo = new \PHP_CodeSniffer\Files\FileList($runner->config, $runner->ruleset);
140+
$numFiles = count($todo);
141+
$numProcessed = 0;
142+
143+
foreach ($todo as $file) {
144+
if ($file->ignored === false) {
145+
try {
146+
$runner->processFile($file);
147+
++$numProcessed;
148+
$runner->printProgress($file, $numFiles, $numProcessed);
149+
} catch (\PHP_CodeSniffer\Exceptions\DeepExitException $e) {
150+
echo $e->getMessage();
151+
152+
return $e->getCode();
153+
} catch (\Exception $e) {
154+
$error = 'Problem during processing; checking has been aborted. The error message was: '.$e->getMessage();
155+
$file->addErrorOnLine($error, 1, 'Internal.Exception');
156+
}
157+
$file->cleanUp();
158+
}
159+
}
85160

86-
$sniffer = new \PHP_CodeSniffer();
87-
$sniffer->setCli($cli);
88-
$sniffer->process($files, $this->standard);
89-
$results = $sniffer->reporting->printReport('full', false, $sniffer->cli->getCommandLineValues(), null, 120);
161+
// Have finished, generate the final reports.
162+
$runner->reporter->printReports();
90163

91164
$maxwarnings = (int) $input->getOption('max-warnings');
92165

93-
return ($results['errors'] > 0 || ($maxwarnings >= 0 && $results['warnings'] > $maxwarnings)) ? 1 : 0;
166+
return ($runner->reporter->totalErrors > 0 || ($maxwarnings >= 0 && $runner->reporter->totalWarnings > $maxwarnings)) ? 1 : 0;
94167
}
95168
}

src/Command/CodeFixerCommand.php

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,22 @@
1212

1313
namespace MoodlePluginCI\Command;
1414

15-
use MoodlePluginCI\Bridge\CodeSnifferCLI;
15+
use PHP_CodeSniffer\Config;
16+
use PHP_CodeSniffer\Reporter;
17+
use PHP_CodeSniffer\Runner;
18+
use PHP_CodeSniffer\Util\Timing;
1619
use Symfony\Component\Console\Input\InputInterface;
1720
use Symfony\Component\Console\Input\InputOption;
1821
use Symfony\Component\Console\Output\OutputInterface;
1922

23+
// Cannot be autoload from composer, because this autoloader is used for both
24+
// phpcs and any standard Sniff, so it must be loaded at the end. For more info, see:
25+
// https://github.com/squizlabs/PHP_CodeSniffer/issues/1463#issuecomment-300637855
26+
//
27+
// The alternative is to, instead of using PHP_CodeSniffer like a library, just
28+
// use the binaries bundled with it (phpcs, phpcbf...) but we want to use as lib for now.
29+
require_once __DIR__.'/../../vendor/moodlehq/moodle-local_codechecker/phpcs/autoload.php';
30+
2031
/**
2132
* Run PHP Code Beautifier and Fixer on a plugin.
2233
*/
@@ -39,23 +50,86 @@ protected function execute(InputInterface $input, OutputInterface $output)
3950
if (count($files) === 0) {
4051
return $this->outputSkip($output, 'No files found to process.');
4152
}
42-
if (!defined('PHP_CODESNIFFER_CBF')) { // Can be defined in tests.
53+
54+
// Needed constant.
55+
if (defined('PHP_CODESNIFFER_CBF') === false) {
4356
define('PHP_CODESNIFFER_CBF', true);
4457
}
4558

46-
$cli = new CodeSnifferCLI([
47-
'reports' => ['cbf' => null],
48-
'colors' => $output->isDecorated(),
49-
'encoding' => 'utf-8',
50-
'files' => $files,
51-
'reportWidth' => 120,
52-
'phpcbf-suffix' => '',
53-
]);
54-
55-
$sniffer = new \PHP_CodeSniffer();
56-
$sniffer->setCli($cli);
57-
$sniffer->process($files, $this->standard);
58-
$sniffer->reporting->printReport('cbf', false, $sniffer->cli->getCommandLineValues(), null, 120);
59+
Timing::startTiming();
60+
61+
$runner = new Runner();
62+
$runner->config = new Config(['--parallel=1']); // Pass a param to shortcut params coming from caller CLI.
63+
64+
// This is not needed normally, because phpcs loads the CodeSniffer.conf from its
65+
// root directory. But when this is run from within a .phar file, it expects the
66+
// config file to be out from the phar, in the same directory.
67+
//
68+
// While this approach is logic and enabled to configure phpcs, we don't want that
69+
// in this case, we just want to ensure phpcs knows about our standards,
70+
// so we are going to force the installed_paths config here.
71+
//
72+
// And it needs need to do it BEFORE the runner init! (or it's lost)
73+
//
74+
// Note: the "moodle" one is not really needed, because it's autodetected normally,
75+
// but the PHPCompatibility one is. There are some issues about version PHPCompatibility 10
76+
// to stop requiring to be configured here, but that's future version. Revisit this when
77+
// available.
78+
//
79+
// Note: the paths are relative to the base CodeSniffer directory, aka, the directory
80+
// where "src" sits.
81+
$runner->config->setConfigData('installed_paths', './../PHPCompatibility');
82+
$runner->config->standards = [$this->standard]; // Also BEFORE init() or it's lost.
83+
84+
$runner->init();
85+
86+
$runner->config->parallel = 1;
87+
$runner->config->reports = ['cbf' => null];
88+
$runner->config->colors = $output->isDecorated();
89+
$runner->config->verbosity = 0;
90+
$runner->config->encoding = 'utf-8';
91+
$runner->config->showProgress = true;
92+
$runner->config->showSources = true;
93+
$runner->config->interactive = false;
94+
$runner->config->cache = false;
95+
$runner->config->extensions = ['php' => 'PHP'];
96+
$runner->config->reportWidth = 132;
97+
98+
$runner->config->files = $files;
99+
100+
$runner->config->setConfigData('testVersion', PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION, true);
101+
102+
// Create the reporter to manage all the reports from the run.
103+
$runner->reporter = new Reporter($runner->config);
104+
105+
// And build the file list to iterate over.
106+
/** @var object[] $todo */
107+
$todo = new \PHP_CodeSniffer\Files\FileList($runner->config, $runner->ruleset);
108+
$numFiles = count($todo);
109+
$numProcessed = 0;
110+
111+
foreach ($todo as $file) {
112+
if ($file->ignored === false) {
113+
try {
114+
$runner->processFile($file);
115+
++$numProcessed;
116+
$runner->printProgress($file, $numFiles, $numProcessed);
117+
} catch (\PHP_CodeSniffer\Exceptions\DeepExitException $e) {
118+
echo $e->getMessage();
119+
120+
return $e->getCode();
121+
} catch (\Exception $e) {
122+
$error = 'Problem during processing; checking has been aborted. The error message was: '.$e->getMessage();
123+
$file->addErrorOnLine($error, 1, 'Internal.Exception');
124+
}
125+
$file->cleanUp();
126+
}
127+
}
128+
129+
// Have finished, generate the final reports and timing.
130+
$runner->reporter->printReports();
131+
echo PHP_EOL;
132+
Timing::printRunTime();
59133

60134
return 0;
61135
}

0 commit comments

Comments
 (0)