Coding Standard Tutorial

Coding Standard Tutorial -- A guide to writing your own code sniffs

Introduction

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.

Creating the Coding Standard Directory

All sniffs in PHP_CodeSniffer must belong to a coding standard. A coding standard is a directory inside PHP_CodeSniffer and a single class, 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:


$ cd /path/to/PHP_CodeSniffer/CodeSniffer/Standards
$ mkdir MyStandard
$ mkdir MyStandard/Sniffs

    

The MyStandard directory represents our coding standard and is the name we will use on the command line to specify the new coding standard to use for checking. 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 class 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 /path/to/PHP_CodeSniffer/CodeSniffer/Standards/MyStandard
$ touch MyStandardCodingStandard.php

    

The content of the MyStandardCodingStandard.php file should be the following:

<?php
/**
 * MyStandard Coding Standard.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Your Name <you@domain.net>
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   CVS: $Id: coding-standard-tutorial.xml,v 1.6 2007/10/15 03:28:17 squiz Exp $
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

require_once 'PHP/CodeSniffer/Standards/CodingStandard.php';

/**
 * MyStandard Coding Standard.
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Your Name <you@domain.net>
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   Release: @package_version@
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */
class PHP_CodeSniffer_Standards_MyStandard_MyStandardCodingStandard extends PHP_CodeSniffer_Standards_CodingStandard
{

}//end class
?>

Note: The coding standard class can be left empty, as it is in this example coding standard. For information about the methods that can be overridden, see the coding standard class documentation.

Creating the Sniff

A sniff requires a single PHP file. It's 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 /path/to/PHP_CodeSniffer/CodeSniffer/Standards/MyStandard/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_Sniff interface so that the PHP_CodeSniffer knows that it should instantiate the sniff once it's invoked. The PHP_CodeSniffer_Sniff interface defines two methods that must be implemented; register() and process().

The register() and process() Methods

The register() method allows a sniff to subscribe to one or more token types that it may wish to process. Once PHP_CodeSniffer encounters one of those tokens, it calls the process() method with the PHP_CodeSniffer_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.

The Token Stack

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_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 (eg. '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: Some tokens have more indexes than those described above. Have a look in the PHP/CodeSniffer/File.php class comment for a list of tokens and their axillary indicies.

Reporting Errors

Once an error is detected, a sniff should indicate that an error has occurred by calling the addError() method on the PHP_CodeSniffer_File object, passing in an appropriate error message as the first argument and the position in the stack where the error was detected as the second. Alternatively, if the violation is considered not as critical as an error, the addWarning() method can be used.

DisallowHashCommentsSniff.php

We now have to write the content of our sniff. The content of the DisallowHashCommentsSniff.php file should be the following:

<?php
/**
 * PHP_CodeSniffer tokenises PHP code and detects violations of a
 * defined set of coding standards.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Your Name <you@domain.net>
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   CVS: $Id: coding-standard-tutorial.xml,v 1.6 2007/10/15 03:28:17 squiz Exp $
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

require_once 'PHP/CodeSniffer/Sniff.php';

/**
 * This sniff prohibits the use of Perl style hash comments.
 *
 * An example of a hash comment is:
 *
 * <code>
 *  # This is a hash comment, which is prohibited.
 *  $hello = 'hello';
 * </code>
 * 
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Your Name <you@domain.net>
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   Release: @package_version@
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */
class MyStandard_Sniffs_Commenting_DisallowHashCommentsSniff implements PHP_CodeSniffer_Sniff
{


    /**
     * Returns the token types that this sniff is interested in.
     *
     * @return array(int)
     */
    public function register()
    {
        return array(T_COMMENT);

    }//end register()


    /**
     * Processes the tokens that this sniff is interested in.
     *
     * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found.
     * @param int                  $stackPtr  The position in the stack where
     *                                        the token was found.
     *
     * @return void
     */
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();
        if ($tokens[$stackPtr]['content']{0} === '#') {
            $error = 'Hash comments are prohibited';
            $phpcsFile->addError($error, $stackPtr);
        }

    }//end process()


}//end class

?>

Results

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

# Check for valid contents.
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) AND 1 WARNING(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
 3 | ERROR | Hash comments are prohibited
 7 | ERROR | Hash comments are prohibited
 9 | ERROR | Hash comments are prohibited
--------------------------------------------------------------------------------