-
-
Notifications
You must be signed in to change notification settings - Fork 60
Coding Standard Tutorial
In this tutorial, we will create a new coding standard with a single sniff. Our sniff will prohibit the use of Perl style hash comments.
All sniffs in PHP_CodeSniffer must belong to a coding standard. A coding standard is a directory with a specific sub-directory structure and a ruleset.xml
file, so we can create one very easily. Let's call our coding standard MyStandard. Run the following commands to create the coding standard directory structure:
$ mkdir MyStandard
$ mkdir MyStandard/Sniffs
As this coding standard directory sits outside the main PHP_CodeSniffer directory structure, PHP_CodeSniffer will not show it as an installed standard when using the -i
command line argument. If you want your standard to be shown as installed, register the MyStandard directory as an external standards with PHP_CodeSniffer:
$ phpcs --config-set installed_paths /path/to/MyStandard
The MyStandard
directory represents our coding standard. The Sniffs
sub-directory is used to store all the sniff files for this coding standard.
Now that our directory structure is created, we need to add our ruleset.xml
file. This file will allow PHP_CodeSniffer to ask our coding standard for information about itself, and also identify this directory as one that contains code sniffs.
$ cd MyStandard
$ touch ruleset.xml
The content of the ruleset.xml
file should, at a minimum, be the following:
<?xml version="1.0"?>
<ruleset name="MyStandard">
<description>A custom coding standard.</description>
</ruleset>
Note
The ruleset.xml can be left quite small, as it is in this example coding standard. For information about the other features that the ruleset.xml
provides, see the Annotated ruleset.
A sniff requires a single PHP file that must be placed into a sub-directory to categorise the type of check it performs. Its name should clearly describe the standard that we are enforcing and must end with Sniff.php
. For our sniff, we will name the PHP file DisallowHashCommentsSniff.php
and place it into a Commenting
sub-directory to categorise this sniff as relating to commenting. Run the following commands to create the category and the sniff:
$ cd Sniffs
$ mkdir Commenting
$ touch Commenting/DisallowHashCommentsSniff.php
Note
It does not matter what sub-directories you use for categorising your sniffs. Just make them descriptive enough so you can find your sniffs again later when you want to modify them.
Each sniff must implement the PHP_CodeSniffer\Sniffs\Sniff
interface so that PHP_CodeSniffer knows that it should instantiate the sniff once it's invoked. The interface defines two methods that must be implemented; register
and process
.
The register
method allows a sniff to subscribe to one or more token types that it wants to process. Once PHP_CodeSniffer encounters one of those tokens, it calls the process
method with the PHP_CodeSniffer\Files\File
object (a representation of the current file being checked) and the position in the stack where the token was found.
For our sniff, we are interested in single line comments. The token_get_all
method that PHP_CodeSniffer uses to acquire the tokens within a file distinguishes doc comments and normal comments as two separate token types. Therefore, we don't have to worry about doc comments interfering with our test. The register
method only needs to return one token type, T_COMMENT
.
A sniff can gather more information about a token by acquiring the token stack with a call to the getTokens
method on the PHP_CodeSniffer\Files\File
object. This method returns an array and is indexed by the position where the token occurs in the token stack. Each element in the array represents a token. All tokens have a code
, type
and a content
index in their array. The code
value is a unique integer for the type of token. The type
value is a string representation of the token (e.g., 'T_COMMENT'
for comment tokens). The type
has a corresponding globally defined integer with the same name. Finally, the content
value contains the content of the token as it appears in the code.
Note
Depending on the token, the token array may contain various additional indexes with further information on a token.
Once an error is detected, a sniff should indicate that an error has occurred by calling the addError
method on the PHP_CodeSniffer\Files\File
object, passing in an appropriate error message as the first argument, the position in the stack where the error was detected as the second, a code to uniquely identify the error within this sniff and an array of data used inside the error message.
Alternatively, if the violation is considered not as critical as an error, the addWarning
method can be used.
We now have to write the content of our sniff. The content of the DisallowHashCommentsSniff.php
file should be the following:
<?php
namespace MyStandard\Sniffs\Commenting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
/**
* This sniff prohibits the use of Perl style hash comments.
*/
final class DisallowHashCommentsSniff implements Sniff
{
/**
* Returns the token types that this sniff is interested in.
*
* @return array<int|string>
*/
public function register()
{
return [T_COMMENT];
}
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* @param int $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['content'][0] === '#') {
$error = 'Hash comments are prohibited; found %s';
$data = [trim($tokens[$stackPtr]['content'])];
$phpcsFile->addError($error, $stackPtr, 'Found', $data);
}
}
}
By default, PHP_CodeSniffer assumes all sniffs are designed to check PHP code only. You can specify a list of tokenizers that your sniff supports, allowing it to be used wth PHP, JavaScript or CSS files, or any combination of the three. You do this by setting the $supportedTokenizers
property in your sniff. Adding the following code to your sniff will tell PHP_CodeSniffer that it can be used to check both PHP and JavaScript code:
/**
* A list of tokenizers this sniff supports.
*
* @var array<string>
*/
public $supportedTokenizers = [
'PHP',
'JS',
];
Now that we have defined a coding standard, let's validate a file that contains hash comments. The test file we are using has the following contents:
<?php
// Slash comments should be ignored.
/* Star comments should also be ignored. */
# Hash comments should be flagged.
if ($obj->contentsAreValid($array)) {
$value = $obj->getValue();
# Value needs to be an array.
if (is_array($value) === false) {
# Error.
$obj->throwError();
exit();
}
}
When PHP_CodeSniffer is run on the file using our new coding standard, 3 errors will be reported:
$ phpcs --standard=MyStandard test.php
FILE: test.php
--------------------------------------------------------------------------------
FOUND 3 ERROR(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
7 | ERROR | Hash comments are prohibited; found # Hash comments should be flagged.
11 | ERROR | Hash comments are prohibited; found # Value needs to be an array.
13 | ERROR | Hash comments are prohibited; found # Error.
--------------------------------------------------------------------------------
For everyone
Introduction
Requirements
Usage
Advanced Usage
Reporting
Configuration Options
Fixing Errors Automatically
FAQ
For coding standard creators
Annotated Ruleset
Customisable Sniff Properties
For sniff developers
Coding Standard Tutorial
Version 3.0 Upgrade Guide
Version 1.3.0 Upgrade Guide