- Notifications
You must be signed in to change notification settings - Fork 1.5k
Added ability to baseline violations#3387
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
base:master
Are you sure you want to change the base?
Added ability to baseline violations #3387
Uh oh!
There was an error while loading. Please reload this page.
Conversation
sergey-solo commented Jul 16, 2021
Just want to point out that this implementation doesn't baseline individual violations. Instead, it baselines specific sniffs for specific files. Which is different from what one might expect :) |
frankdekker commented Jul 16, 2021
Correct, will point that out in the documentation. It's a technical choice I took to avoid basing it on line number. Because that will become quickly annoying when you modify files :). |
gsherwood commented Jul 18, 2021
I'm not convinced that this is a useful way of doing a baseline. When entire sniffs (or even sniff codes) are baselined for a file, adding new code with violations to that file wont show any errors if they happen to be the same sort of errors already in the file. For projects that need a baseline, or when introducing a new check to a standard, I think this is a pretty likely case. I'd likely only accept a baseline feature if it was able to distinguish between violations in existing code and violations in new code. Anything else feels like an ongoing support issue for me as the feature wont work the way people expect and I'll have to defend the design decision for it. I'm absolutely happy to do this, but only if I believe the feature works the way it should. This is one of those things where we really need a discussion about the solution design before a PR. There was some of that back on #2543, but it's been a while and no real solution was ever outlined. If you want to give this a go, it would be a terrific feature to have and I'd be happy to discuss any ideas you have for the solution over on that other issue. |
frankdekker commented Jul 19, 2021 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Completely understandable. This baseline mechanism will indeed suppress new issues in the same file. I'm happy to brainstorm how to make this feature more robust. The problem at hand is that we need a unique way to detect where the violations came from within the file, without explicitly counting tokens or line numbers. I'll propose an approach in #2543 |
frankdekker commented Jul 31, 2021 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@gsherwood Now a code signature will be calculated and added to the baseline report file. The code signature is the hash of the code on the line of the violation, and 1 line before and after. During standard inspection when comparing against the baseline, the same signature will be calculated for encountered violations and compared against the baseline signature. The violation will only be skipped if the code signature is identical to the one in the baseline. As mentioned, this will prevent a sniff being disabled for the entire file. The sniff will now be specifically disabled for the calculated code signature. Modifications in the file won't break the baseline, unless you edit one of the 3 lines around the baseline. At this point I feel it's quite useful to resolve the violation anyway, as you're editing that close to it. I would love to hear your thoughts about this solution. Regards Frank Dekker Example baseline:<?xml version="1.0" encoding="UTF-8"?> <phpcs-baselineversion="3.6.1"> <violationfile="src\Baseline\BaselineSet.php"sniff="PEAR.Commenting.FunctionComment.MissingParamName"signature="54accce13bb236dc361cf9d75895c274fce619d5"/> <violationfile="src\Baseline\BaselineSet.php"sniff="PEAR.Commenting.FunctionComment.MissingParamTag"signature="25a91da1dadc60a04a9a6615502ad4aa4fd397c2"/> <violationfile="src\Baseline\BaselineSet.php"sniff="PEAR.Functions.FunctionDeclaration.SpaceBeforeOpenParen"signature="edcfdf132eb46b842349404740ba7b8f224d82e1"/> </phpcs-baseline> |
frankdekker commented Oct 9, 2021
@gsherwood It's been a while. You had any chance yet to look at the latest proposal with the code signatures? |
Potherca commented Mar 8, 2022
Being able to have a baseline would be much appreciated. @gsherwood Is there anything that can be done to move this forward? |
frankdekker commented Mar 8, 2022 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@Potherca Might be an nice alternative for now. |
Housik commented Apr 24, 2022
@gsherwood Hi Greg, would you be please so kind and take a look to Frank solution? It looks like it now mets your requirements about baseline functionality for phpcs. I am one of the many waiting for this feature, it will help us to rewrite large project. Thank you in advance! Martin ;-) |
gsherwood commented Apr 25, 2022
@Housik This isn't something I have time to look at. |
BafS commented Oct 12, 2022
Is there a chance to see this PR merged? It would be really useful for us too, especially when working with big codebases. |
frankdekker commented Oct 12, 2022
You can use https://github.com/123inkt/php-codesniffer-baseline in the meantime. |
BafS commented Nov 1, 2022
@frankdekker unfortunately it didn't work well with our CI (because of editing a vendor file, which should be immutable). I created a new report for that, without having the need to change a vendor, it's hacky but it works: <?phpdeclare(strict_types=1); usePHP_CodeSniffer\Files\File; usePHP_CodeSniffer\Reports\Full; usePHP_CodeSniffer\Reports\Report; finalclass BaselineReport implements Report{publicconstBASELINE_FILE = __DIR__ . '/baseline.php'; privateFull$fullReport; publicfunction__construct(){$this->fullReport = newFull()} privatefunctioncleanReportFromBaseline(array$baseline, array$report): array{$filename = $report['filename']; if (!isset($baseline[$filename])){return$report} $baselineSources = $baseline[$filename]; $messages = &$report['messages']; foreach ($messagesas$ka => $messageLine){foreach ($messageLineas$kb => $messagesOff){foreach ($messagesOffas$kc => $messageList){$source = $messageList['source'] ?? ''; if (isset($baselineSources[$source]) && $baselineSources[$source]['occurrences'] > 0){$type = $messages[$ka][$kb][$kc]['type']; // Decrement the warnings/errors to keep in sync with the removal of messages in the reportmatch ($type){'WARNING' => $report['warnings']--, 'ERROR' => $report['errors']--, }; unset($messages[$ka][$kb][$kc]); $baselineSources[$source]['occurrences']--} } } } return$report} publicfunctiongenerateFileReport($report, File$phpcsFile, $showSources = false, $width = 80): bool{$baseline = []; if (is_file(self::BASELINE_FILE)){$baseline = requireself::BASELINE_FILE; if (!is_array($baseline)){$baseline = []} } $report = $this->cleanReportFromBaseline($baseline, $report); return$this->fullReport->generateFileReport($report, $phpcsFile, $showSources, $width)} publicfunctiongenerate($cachedData, $totalFiles, $totalErrors, $totalWarnings, $totalFixable, $showSources = false, $width = 80, $interactive = false, $toScreen = true): void{// `Full` report only uses $cachedData$this->fullReport->generate($cachedData, $totalFiles, $totalErrors, $totalWarnings, $totalFixable, $showSources, $width, $interactive, $toScreen)} }Usable like: And to generate the baseline <?phpdeclare(strict_types=1); usePHP_CodeSniffer\Files\File; usePHP_CodeSniffer\Reports\Report; useApp\Serializer\PhpEncoder; finalclass GenerateBaselineReport implements Report{privatestaticarray$baseline = []; publicfunctiongenerateFileReport($report, File$phpcsFile, $showSources = false, $width = 80): bool{$filename = $report['filename']; /** @var array<string, int> $occurrences */$occurrences = []; $messages = &$report['messages']; foreach ($messagesas$messageLine){foreach ($messageLineas$messagesOff){foreach ($messagesOffas$messageList){$occurrences[$messageList['source']] ??= 0; $occurrences[$messageList['source']]++} } } foreach ($occurrencesas$source => $occurrence){self::$baseline[$filename][$source]['occurrences'] = $occurrence} returntrue} publicfunctiongenerate( $cachedData, $totalFiles, $totalErrors, $totalWarnings, $totalFixable, $showSources = false, $width = 80, $interactive = false, $toScreen = true, ): void{global$argv; $phpEncoder = newPhpEncoder(); $out = $phpEncoder->encode(self::$baseline, PhpEncoder::FORMAT, [ 'strict' => true, ]); $inputString = implode('', $argv); $out = str_replace('return ', "// Generated with: $inputString\nreturn ", $out); echo$out} }Usable like: |
peterjaap commented Nov 20, 2022 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@frankdekker it would be great to also have notices for false positives. See my explanation here; 123inkt/php-codesniffer-baseline#7 PHPStan also does this, it's called "unmatched ignored errors", see https://phpstan.org/user-guide/baseline#generating-the-baseline @gsherwood what can we do for you to move this PR forward? Maybe @jrfnl can take a look since for some reason primarily Dutch people are active in this PR? 😃 |
peterjaap commented Nov 20, 2022
@BafS I wanted to give your solution a whirl, but I'm missing the |
BafS commented Nov 20, 2022
@peterjaap right, it's from |
forrest79 commented Jan 27, 2023
Hi to all, thanks for this PR and the discussion in it. Like you, we also need a baseline and better ability to ignore valid errors. I look over all your solutions and prepared a package, that works in a similar way as PHPStan (also with outdated info). There must still be some magic and hacking, but it works without hard changing files in the vendor. We're using this for a couple of weeks in our production CI and everything looks good. You can find it here https://github.com/forrest79/PHPCS-Ignores. I will be glad for your comment and improvement. Still, I'm hoping, that PHPCS adds something like this as a new feature :-) |
frankdekker commented Jan 28, 2023
Hihi, also nice trick to hook into PHPCS reporting. And same here, still hoping PHPCS comes with a build-in implementation :). |
Morgy93 commented Sep 6, 2023
This is an interesting approach: https://github.com/isaaceindhoven/php-code-sniffer-baseliner Alternatives: |
DanielRuf commented May 5, 2025 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Does anybody know, if there was some relevant progress for PHPCS baseline generation? What is currently used by most and recommended regarding this?
|
peterjaap commented May 7, 2025
We use the 123inkt one! Just run this; And you're good to go. I'm not sure how composer patches would help here? |
DanielRuf commented May 7, 2025
@peterjaap regarding composer-patches:
That a package deliberately modifies code of another package is quite interesting. Not sure if this behavior is allowed for composer packages by design and if this will work forever. Applying some patch via |
peterjaap commented May 7, 2025
@DanielRuf yes that's also a way to go. I prefer having it in one package though so its more easily trackable, but that's personal preference. |
frankdekker commented May 7, 2025 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
I do not know if it's allowed either, but I got this idea from https://github.com/slevomat/coding-standard which uses: This packagewill create/update I agree modifying code is a whole other level, hence the disclaimer in the package |
tomcat0090 commented Sep 11, 2025
I'm looking at this pull request with eager eyes. |
Added the ability to baseline violations. #2543
Features
Create baseline
phpcs src --report-baseline=phpcs.baseline.xml --basepath=/path/to/project/rootphpcs.baseline.xmlin the current working directory. Pref be the root of the projectbasepathwill be subtracted from the filepaths in the baseline xml.Use default baseline
Will by default search in the working directory for
phpcs.baseline.xmlphpcs srcUse custom location baseline
phpcs src --baselineFile=<path/to/baseline.xml>Changes
Added classes to read the baseline file and setup data structure.
Added check in File::addMessage to check if the message is baselined.
Added
--baselineFileto cli optionsAdded unit tests for the newly added files.
Documentation
My question, how do I update the documentation to add a section about the baseline feature?