Skip to content

Commit 901be9d

Browse files
Update codesniffer package to work with SonarQube
1 parent 313ba69 commit 901be9d

5 files changed

Lines changed: 1232 additions & 0 deletions

File tree

.idea/php.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,79 @@ vendor/bin/phpunit
152152
There is also a pre-commit hook for git to run Code Sniffer on commit.
153153

154154
Check out the [instructions](git-hook) on how to use it.
155+
156+
### SonarQube rule sync
157+
158+
The `tools/` folder contains two files that keep the Totara PHPCS ruleset aligned with the "Totara standards" SonarQube quality profiles:
159+
160+
| File | Purpose |
161+
|---|---|
162+
| `tools/sonarqube-phpcs-map.json` | Translation table: every active Sonar rule key → its PHPCS sniff equivalent (or `[]` if no PHPCS equivalent exists). Covers all 4 profiles: PHP (228 rules), JS (403), CSS (27), Web/HTML (53). |
163+
| `tools/sync-sonarqube-rules.php` | CLI script that reads a Sonar profile export, cross-references the map, and reports which rules are covered, missing, or unmapped. |
164+
165+
These files are **not needed to run PHPCS** — the ruleset works independently. They are maintenance tools: use them when the Sonar profile changes or when you want to audit coverage.
166+
167+
#### Step 1 — Export the SonarQube profile
168+
169+
1. In SonarQube Server, go to **Quality Profiles** (top menu).
170+
2. Select language **PHP** (or JS / CSS / HTML as needed).
171+
3. Open the **"Totara standards"** profile.
172+
4. Click **Actions** (top-right `...`) → **Back up**.
173+
5. Save the downloaded `.xml` file locally.
174+
175+
#### Step 2 — Run the sync checker
176+
177+
```bash
178+
cd /path/to/code-sniffer
179+
180+
# Check PHP profile
181+
php tools/sync-sonarqube-rules.php --profile=/path/to/Totara_standards_php.xml
182+
183+
# Check with explicit map and ruleset paths (all defaults shown)
184+
php tools/sync-sonarqube-rules.php \
185+
--profile=/path/to/Totara_standards_php.xml \
186+
--map=tools/sonarqube-phpcs-map.json \
187+
--ruleset=src/Standards/Totara/ruleset.xml
188+
189+
# Show help
190+
php tools/sync-sonarqube-rules.php --help
191+
```
192+
193+
#### Step 3 — Interpret the output
194+
195+
```
196+
Sonar rules in profile: 228
197+
Covered in Totara ruleset: 46 ← enforced by an active PHPCS sniff
198+
Mapped but missing in ruleset: 2 ← mapped in the JSON but sniff not added to ruleset.xml yet
199+
Unmapped Sonar rules: 183 ← no PHPCS equivalent exists (security taint, runtime checks, etc.)
200+
201+
Mapped but missing in ruleset:
202+
- php:S1234 => Generic.Some.Sniff
203+
204+
Unmapped Sonar rules:
205+
- php:S2068
206+
- phpsecurity:S2076
207+
...
208+
```
209+
210+
| Output line | What to do |
211+
|---|---|
212+
| **Covered** | Nothing — already enforced by PHPCS. |
213+
| **Mapped but missing in ruleset** | Add the listed PHPCS sniff ref to `src/Standards/Totara/ruleset.xml`. |
214+
| **Unmapped** | Either add a mapping in `tools/sonarqube-phpcs-map.json` if a PHPCS sniff exists, or leave as `[]` — these are rules PHPCS architecturally cannot check (security taint analysis, JS/CSS/HTML, deep flow analysis). |
215+
216+
#### Step 4 — Update the map and ruleset
217+
218+
If you find a Sonar rule that has a PHPCS equivalent but is not yet mapped:
219+
220+
1. Open `tools/sonarqube-phpcs-map.json`.
221+
2. Find the Sonar rule key and replace `[]` with the PHPCS sniff ref string, e.g.:
222+
```json
223+
"php:S1234": "Generic.Some.Sniff"
224+
```
225+
3. Add the sniff to `src/Standards/Totara/ruleset.xml`:
226+
```xml
227+
<!-- SonarQube rule coverage (php:S1234) -->
228+
<rule ref="Generic.Some.Sniff"/>
229+
```
230+
4. Re-run the sync script to confirm the rule moves from **Unmapped** to **Covered**.

src/Standards/Totara/ruleset.xml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,103 @@
189189
<!-- Other operators are left undefined. -->
190190
<rule ref="Totara.Operators.OperatorSpacing"/>
191191

192+
<!-- SonarQube rule coverage (php:S107, php:S110, php:S134, php:S2004) -->
193+
<!-- Nesting depth and complexity checks -->
194+
<rule ref="Generic.Metrics.NestingLevel">
195+
<properties>
196+
<property name="nestingLevel" value="5"/>
197+
<property name="absoluteNestingLevel" value="10"/>
198+
</properties>
199+
</rule>
200+
<rule ref="Generic.Metrics.CyclomaticComplexity">
201+
<!-- php:S3776 Cognitive/cyclomatic complexity -->
202+
<properties>
203+
<property name="complexity" value="15"/>
204+
<property name="absoluteComplexity" value="30"/>
205+
</properties>
206+
</rule>
207+
208+
<!-- SonarQube rule coverage (php:S1068, php:S1172) -->
209+
<!-- Unused parameters -->
210+
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
211+
212+
<!-- SonarQube rule coverage (php:S1116) -->
213+
<!-- Empty PHP statements (;;) -->
214+
<rule ref="Generic.CodeAnalysis.EmptyPHPStatement"/>
215+
216+
<!-- SonarQube rule coverage (php:S1186, php:S905) -->
217+
<!-- Empty catch/if/else bodies -->
218+
<rule ref="Generic.CodeAnalysis.EmptyStatement"/>
219+
220+
<!-- SonarQube rule coverage (php:S1121) -->
221+
<!-- Assignment in condition -->
222+
<rule ref="Generic.CodeAnalysis.AssignmentInCondition"/>
223+
224+
<!-- SonarQube rule coverage (php:S1145) -->
225+
<!-- if (true) / if (false) unconditional -->
226+
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
227+
228+
<!-- SonarQube rule coverage (php:S1185) -->
229+
<!-- Useless overriding methods that just call parent -->
230+
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
231+
232+
<!-- SonarQube rule coverage (php:S1110) -->
233+
<!-- Unnecessary parentheses -->
234+
<rule ref="Generic.WhiteSpace.ArbitraryParenthesesSpacing"/>
235+
236+
<!-- SonarQube rule coverage (php:S1134) -->
237+
<!-- FIXME comments -->
238+
<rule ref="Generic.Commenting.Fixme"/>
239+
240+
<!-- SonarQube rule coverage (php:S1135) -->
241+
<!-- TODO comments -->
242+
<rule ref="Generic.Commenting.Todo"/>
243+
244+
<!-- SonarQube rule coverage (php:S125) -->
245+
<!-- Commented-out code -->
246+
<rule ref="Squiz.PHP.CommentedOutCode">
247+
<properties>
248+
<property name="maxPercentage" value="35"/>
249+
</properties>
250+
</rule>
251+
252+
<!-- SonarQube rule coverage (php:S1763, php:S881) -->
253+
<!-- Dead/non-executable code after return/throw/etc -->
254+
<rule ref="Squiz.PHP.NonExecutableCode"/>
255+
256+
<!-- SonarQube rule coverage (php:S139) -->
257+
<!-- Inline comments after statements -->
258+
<rule ref="Squiz.Commenting.PostStatementComment"/>
259+
260+
<!-- SonarQube rule coverage (php:S1788) -->
261+
<!-- Default parameter values must not follow non-default params -->
262+
<rule ref="PEAR.Functions.ValidDefaultValue"/>
263+
264+
<!-- SonarQube rule coverage (php:S907) -->
265+
<!-- Discourage goto -->
266+
<rule ref="Generic.PHP.DiscourageGoto"/>
267+
268+
<!-- SonarQube rule coverage (php:S2068) -->
269+
<!-- No hardcoded credentials / passwords in code -->
270+
<rule ref="Generic.PHP.ForbiddenFunctions"/>
271+
272+
<!-- SonarQube rule coverage (php:S1192) -->
273+
<!-- Unnecessary string concatenation -->
274+
<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
275+
276+
<!-- SonarQube rule coverage (php:S1301) -->
277+
<!-- Single-case switch statements -->
278+
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
279+
280+
<!-- SonarQube rule coverage (php:S1155, php:S3699) -->
281+
<!-- Duplicate class names -->
282+
<rule ref="Generic.Classes.DuplicateClassName"/>
283+
284+
<!-- SonarQube rule coverage (php:S2612) -->
285+
<!-- No silenced errors (@) -->
286+
<rule ref="Generic.PHP.NoSilencedErrors"/>
287+
288+
<!-- Git merge conflicts left in code -->
289+
<rule ref="Generic.VersionControl.GitMergeConflict"/>
290+
192291
</ruleset>

0 commit comments

Comments
 (0)