Last time we looked at some sample CodeSniffer reports and in the source report we saw that the Zend standard was also reporting errors from other standards.
$ phpcs --report=source --standard=Zend ./_rr PHP CODE SNIFFER VIOLATION SOURCE SUMMARY -------------------------------------------------------------------------------- STANDARD CATEGORY SNIFF COUNT -------------------------------------------------------------------------------- Zend Files Line length 92 Generic White space Disallow tab indent 65 Zend Naming conventions Valid variable name 59 PEAR White space Scope closing brace 33 PEAR Functions Function call signature 26 PEAR Control structures Control signature 12 PEAR Files Line endings 11 PEAR Functions Function call argument spacing 5 Generic PHP Disallow short open tag 1 Zend Files Closing tag 1 -------------------------------------------------------------------------------- A TOTAL OF 305 SNIFF VIOLATION(S) WERE FOUND IN 10 SOURCE(S) --------------------------------------------------------------------------------
Each CodeSniffer standard is comprised of rules or sniffs. A standard can contain a sniff from other standards.
This allows us to quickly and easily create our own custom standard by leveraging those rules that are already contained in existing standards.
To find out what we have available to use, we can looking for all the files that end in Sniff.php within the Standards folder. We can see we have 167 sniff available to use already (with version 1.2.0a1 anyway).
Full list of available Sniffs
cd {your pear path}/PHP/CodeSniffer/Standards $ find ./ -name "*Sniff.php" -and -not -name "Abstract*"
Clicking here to see the full list of available Sniffs.
Some of these Sniffs are self explanatory some are less so, and would need a cursory glance at the code to see what they are checking for.
Luckily most of the classes are well documented (as the PHPCS standard dictates!) so looking at the class phpdoc comment usually tells what the sniff is looking for.
Something to note is that you will find some of the sniffs are mutually exclusive:
e.g.
./Generic/Sniffs/Formatting/NoSpaceAfterCastSniff.php ./Generic/Sniffs/Formatting/SpaceAfterCastSniff.php ./Generic/Sniffs/Functions/OpeningFunctionBraceBsdAllmanSniff.php ./Generic/Sniffs/Functions/OpeningFunctionBraceKernighanRitchieSniff.php
Writing the standard
So to start with we need to come up with a name for the standard.
So for this example I’m going to use KingKludge.
Now I prefer to develop in my home dir, but to get this sniff recognised you will need to have the standard available in {your pear path}/PHP/CodeSniffer/Standards/.
So I symlink my standard into the pear path, but you could work directly in the pear folder.
$ mkdir KingKludge $ ln -s KingKludge {your pear path}/PHP/CodeSniffer/Standards/KingKludge
Then we need to create the standard file. Which follows the naming convention {standard}CodingStandard.php so we end up with the following file.
The underscores in the class name are important as the autoload function splits the path at underscores, so if you miss-type one of the path parts you will get a fatal error when the class attempts to load.
Assuming that you did have your class in the correct folder you can do run.
$ phpcs -i
And you should see your new standard is listed as available for use.
As a timesaving tip, to stop us from having to type the standard name every time you can run CodeSniffer run the following command.
$ phpcs --config-set default_standard KingKludge
Now unless we override with the --standard
switch CodeSniffer will default to using the KingKludge standard.
Adding some rules
So now we have a new standard but it doesn’t do anything yet, as it has no rules to apply.
We have two methods in the class to define the rules or sniffs getIncludedSniffs()
and getExcludedSniffs()
. Not unsurprisingly these methods should return an array of the sniffs to include or exclude.
If we decided we wanted to make life easy for ourselves and base our standard on someone else’s standard, we can quite simply include all of their standard and then swap out the sniffs we don’t want for others we do.
For example, taking the Squiz coding standard.
public function getIncludedSniffs() { return array( 'Squiz', ); }
Now say we want to swap the BSD style bracing rule for K&R style braces, and we don’t want the CodeAnalyzer rule to run either.
So we add the K&R braces Sniff to our include and add the CodeAnalyzer and BSD braces sniffs to the exclude.
public function getIncludedSniffs() { return array( 'Squiz', 'Generic/Sniffs/Functions/OpeningFunctionBraceKernighanRitchieSniff.php', ); } public function getExcludedSniffs() { return array( 'Generic/Sniffs/Functions/OpeningFunctionBraceBsdAllmanSniff.php', 'Zend/Sniffs/Debug/CodeAnalyzerSniff.php', ); }
That seems easy enough!
So now you can see how easily you can create your own standards by picking as few or as many sniffs as you want from existing standards.
Next time, understanding the internals of CodeSniffer and how it works.
Really nice. I try this for myself and run into a problem. I include PEAR and the KernighanRitchie Sniff – similar to your explanation. Further I excluded the BSDAllmanSniff. So, I should have the PEAR Standard with changed braces-coding convention.
But I get the error, that the braces should be on a new line, so the ExcludedSniffs seems not to be noted by the codesniffer.
Is there some trick, I didn’t got?
regards
Hmmm, that’s interesting.
What version of CodeSniffer are you running, I’ll give it a try as well. If I get the same it may well be a bug.
In which case I’ll raise a bug against it.
Bob
Hello,
I have read your 3 articles about codesniffer, but this last one ‘Writing an example CodeSniffer Standard’ I do not understood.
I need to write a standard with only what I want to check and not adapt from an existing standard.
For example, I need to check if the “{” and “}” are after the line of the IF command and not in front of it. and other simple checks, but I do not understood on how to find these commands and where to put them to work.
Could you give me a direction where I’ll find this?