コーディング規約のチュートリアル

コーディング規約のチュートリアル – 独自の規約を作成するための手引き

導入

このチュートリアルでは、 規則をひとつだけ持った新しいコーディング規約を作成していきます。 作成する規則は、Perl 風のハッシュ記号によるコメントを禁止するものです。

コーディング規約用のディレクトリの作成

PHP_CodeSniffer で使用する規約は、すべてコーディング規約に従っている必要があります。 この規約とは、特定のサブディレクトリ構造と ruleset.xml ファイルを持たなければならないということです。 というわけなので、非常に簡単に作成することができます。 今回作成するコーディング規約は MyStandard という名前にしましょう。次のコマンドで、このコーディング規約用のディレクトリ構造を作成します。

    

$ 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, create the MyStandard directory inside the PHP_CodeSniffer install:

    

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

MyStandard ディレクトリが 今回作成するコーディング規約そのものを表します。 サブディレクトリ Sniffs には、 このコーディング規約が使用するすべての sniff ファイルを保存します。

ディレクトリ構造ができたので、クラスファイルを追加していきましょう。 このファイルによって PHP_CodeSniffer がコーディング規約の情報を問い合わせられるようになり、 このディレクトリが sniff を含むものであることを示します。

    

$ cd MyStandard
$ touch ruleset.xml

ruleset.xml ファイルの中身は次のようになります。

<?xml version="1.0"?>
<ruleset name="MyStandard">
 <description>A custom coding standard.</description>
</ruleset>
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.xml.

Creating the Sniff

各 sniff は、それぞれ個別の PHP ファイルを使用します。 その名前は、扱っている規約の内容がはっきりわかるようなものにします。 また、名前の最後は Sniff.php としなければなりません。 今回の場合は、PHP のファイル名は DisalowHashCommentsSniff.php とし、サブディレクトリ Commenting に配置します。 これにより、この規約がコメントに関するものであることを表しています。 以下のコマンドで、カテゴリおよび sniff ファイルを作成します。

    

$ cd Sniffs
$ mkdir Commenting
$ touch Commenting/DisallowHashCommentsSniff.php
カテゴリ分けにどんなサブディレクトリを使用するかは決まっていません。 わかりやすい名前にしておき、あとで修正する際にそれを見つけやすいようにしておきましょう。

各 sniff ファイルは PHP_CodeSniffer_Sniff インターフェイスを実装している必要があります。これにより、 PHP_CodeSniffer はそれが sniff であることを知り、 起動時にインスタンスを作成します。 PHP_CodeSniffer_Sniff で定義されているメソッドは register() および process() のふたつで、これらは必ず実装しなければなりません。

register() および process() メソッド

register() メソッドでは、 処理の対象となるトークンの型を sniff に登録します。 PHP_CodeSniffer がこれらのトークンに遭遇すると、 PHP_CodeSniffer_File オブジェクト (現在チェック中のファイルを表します) およびスタック内でそのトークンが見つかった位置を指定して process() メソッドをコールします。

今回作成する sniff で対象としているのは、単一行コメントです。PHP_CodeSniffer がトークンの取得に使用している token_get_all() メソッドは、doc コメントと通常のコメントを別のトークン型として判定します。 そこで、doc コメントは今回のテストでは気にしないことにします。 register() メソッドは、単一のトークン型 T_COMMENT を返すようにする必要があります。

トークンスタック

sniff は、トークンに関するより多くの情報を取得するためにトークンスタックを使用することができます。 そのためには、PHP_CodeSniffer_File オブジェクトの getTokens() メソッドをコールします。 このメソッドは配列を返します。そのインデックスが、 トークンスタック内でのトークンの現れる位置を表します。 配列の各要素が、トークンを表します。 すべてのトークンは配列形式で、 codetype および content というインデックスを持っています。 code の値は、トークンの型を表す一意な整数値です。 type の値は、トークンの型を表す文字列です (たとえば 'T_COMMENT' はコメントトークンを表します)。 type は、その名前に対応するグローバルに定義された整数値も保持します。 また、content の値には、 コード内で現れたトークンの内容が含まれます。

トークンによっては、上で説明したもの以外のインデックスを持っているものもあります。 PHP/CodeSniffer/File.php クラスのコメントで、 トークンの一覧とそのインデックスを参照ください。

エラーレポート

エラーが見つかったら、sniff はそれを通知しなければなりません。そのためには、 PHP_CodeSniffer_File オブジェクトの addError() メソッドをコールします。その際に、 適切なエラーメッセージを最初の引数、 エラーが発生した箇所のスタック内での位置を二番目の引数として指定します。 その違反がエラーというほど深刻ではない場合には、 addWarning() メソッドを使用します。

DisallowHashCommentsSniff.php

では、sniff の中身を書いていきましょう。 DisallowHashCommentsSniff.php ファイルの内容は、次のようになります。

<?php
/**
 * This sniff prohibits the use of Perl style hash comments.
 *
 * 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   SVN: $Id: coding-standard-tutorial.xml,v 1.9 2008-10-09 15:16:47 cweiske Exp $
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

/**
 * 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; found %s';
            
$data  = array(trim($tokens[$stackPtr]['content']));
            
$phpcsFile->addError($error$stackPtr'Found'$data);
        }

    }
//end process()


}//end class

?>
デフォルトでは、PHP_CodeSniffer はすべての sniff が PHP のコードのみをチェックするものとみなします。 自作の sniff でサポートするトークナイザの一覧を指定し、 それらが PHP のコード用なのか JavaScript のコード用なのか両方用なのかを指定することができます。 それを設定するのが、自作の sniff 内のメンバ変数 $supportedTokenizers です。 次のコードを sniff に追加すると、PHP_CodeSniffer に対してそれが PHP と JavaScript の両方で使えることを指定できます。

<?php
/**
 * A list of tokenizers this sniff supports.
 *
 * @var array
 */
public $supportedTokenizers = array(
                               
'PHP',
                               
'JS',
                              );
?>

結果

これでコーディング規約が定義できたので、 ハッシュ形式のコメントを含むファイルの検証をしてみましょう。

ここでは次のような内容のテストファイルを使用するものとします。

<?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();
    }
}

?>

PHP_CodeSniffer を実行してこのファイルを新たなコーディング規約でチェックすると、 3 つのエラーが発生します。


$ phpcs --standard=/path/to/MyStandard test.php

FILE: test.php
--------------------------------------------------------------------------------
FOUND 3 ERROR(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
 3 | ERROR | Hash comments are prohibited; found # Check for valid contents.
 7 | ERROR | Hash comments are prohibited; found # Value needs to be an array.
 9 | ERROR | Hash comments are prohibited; found # Error.
--------------------------------------------------------------------------------
コマンドラインで渡したコーディング規約ディレクトリが、絶対パスであったことに注意しましょう。 というのも、今回の規約は PHP_CodeSniffer のディレクトリ構造とは別の場所にインストールされているからです。 PHP_CodeSniffer の内部に作成した場合は、単純にその規約名を渡すだけでかまいません。

    

$ phpcs --standard=MyStandard Test.php
設定オプションの一覧 (Previous) A sample ruleset.xml file that describes all features of the format (Next)
Last updated: Fri, 18 Apr 2014 — Download Documentation
Do you think that something on this page is wrong? Please file a bug report or add a note.
View this page in:

User Notes:

Note by: tbannister
Hi Don, please check the version of PHP CS that you're running. Versions earlier than 1.3 used an xxxCodingStandard.php file instead of the new ruleset.xml which was introduced in 1.3.
Note by: pg.allen@gmail.com
I ran into an interesting use case; I'd like to use the normal PEAR install, but be able to define custom standards that are part of our cvs project for the code base. Thus, CodeSniffer hits a failure when it can't include my custom sniffs because they aren't in the normal place and also aren't in php.ini's include_path. Since each developer needs to run phpcs against local files in various cvs sandboxes, we can't just include the path in php.ini (it varies). I've implemented a fix, a command-line option called "--sniffs-dir" that tells getSniffFiles() to also look in the --sniffs-dir directory. In CLI.php, I also catch that option and append it to the include_path. Seems to work fine. Is anyone interested in adding this feature to this awesome tool?
Note by: Don Turner
When creating a new standard e.g. MyStandard you need to put the class MyStandardCodingStandard.php into the MyStandard folder which you have just created.

This took me a bit of time to figure out, perhaps someone could update the documentation to reflect this.