为你的 PHP_CodeSniffer 构建自定义规则

什么是PHP_CodeSniffer?

PHP_CodeSniffer 是 PEAR 中的一个用检测PHP代码是否符合编码规范的扩展包. 它可以促进你写出符合编码规范的代码, 也可以实现代码审查中对编码规范审查自动检测工作. 总的来说, 就是个犀利的工具.

sniffs(嗅探规则)

PHP_CodeSniffer 里的编码检测规则称之为 sniffs (嗅探规则).

HOW TO

1. 创建编码规范的目录

进入 PHP_CodeSniffer 安装目录下的 Standards 目录, 创建编码规范目录以及用于存放规范对应的嗅探规则的 Sniffs 目录, 以我本地 PHP_CodeSniffer 安装路径为例:
cd /usr/local/php/lib/php/PHP/CodeSniffer/Standards
mkdir -p XWSoulStandard/Sniffs

2. 创建编码规则信息文件

进入编码规则目录, 新建 ruleset.xml 文件, 该文件用于存储编码规则的信息以供 PHP_CodeSniffer 读取, 同时作为表示当前目录是编码标准目录的标识.
cd XWSoulStandard
touch ruleset.xml

输入编码规范的相关信息, 大致如下
<?xml version="1.0"?>
<ruleset name="XWSoulStandard">
<description>xwsoul's coding standard.</description>
</ruleset>

这里我们就采用 ruleset.xml 的最小配置, 更多信息请参照 ruleset 附录.

3. 创建嗅探规则文件

一个嗅探规则就是一个PHP文件, 规则文件的命名应当清晰的描述规则的作用, 文件名必须以 Sniff.php 结尾. 在这里我们要创建一个 DisallowHashCommentsSniff.php 的 PHP 文件, 并把他放在 Commenting 子目录下, 用以将这个嗅探规则归类到和评论相关, 具体指令如下:
cd Sniffs
mkdir Commenting
touch Commenting/DisallowHashCommentsSniff.php

嗅探规则的分类子目录不是强制限定的, 只不过这么做在你想要修改/更正规则的时候, 便于你找到它.

4. 编写嗅探规则

每一个嗅探规则都要实现 PHP_CodeSniffer_Sniff 接口, 这样 PHP_CodeSniffer 才会知道规则一旦被调用就该实例化他. PHP_CodeSniffer_Sniff 接口定义的 register() 和 process() 两个方法必须被实现.

5. register() 和 process() 方法

register()方法允许嗅探规则定阅一个或多个它想处理的令牌类型(token types).一旦 PHP_CodeSniffer 遇到了那些令牌中的一个, 它就会调用携带 PHP_CodeSniffer_File 对象的process() 方法, 然后令牌在堆栈中的位置就被找到了.
这里的 sniff 我们重点在处理单行评论. PHP_CodeSniffer 用于在一个文件里提取令牌的的 token_get_all() 方法将文档注释和普通注释识别为两种不同的令牌类型. 因此我们不必担心文档注释会干涉到我们的测试. register() 只需要返回一种令牌类型 T_COMMENT 就可以了.

6. 令牌堆栈

一个嗅探规则可以通过调用 PHP_CodeSniffer_File 对象上的 getTokens() 方法提取令牌堆栈来收集一个令牌的更多的信息. 这个方法返回了一个数组然后会以令牌在令牌堆栈里触发的位置来索引. 所有的令牌数组都有一个代码, 类型以及内容索引. 代码的值是一个令牌类型唯一的整数. 类型的值是一个描述令牌的字符串(如, ‘T_COMMENT’是评论的令牌.)每个类型都有一个对应的使用相同名字的全局定义的整数。最后, 内容的值包含了令牌在代码中展现的内容.

可以看看 PHP/CodeSniffer/File.php 的类注释里标注的完整的令牌的索引列表.

7. 错误报告

一旦错误被检测出来, 嗅探规则会调用 PHP_CodeSniffer_File 上的 addError() 方法, 传入对应的错误消息作为第一参, 错误在堆栈中发生的位置作为第二参, 本嗅探规则中用于做错误唯一标识的代码以及一个用以替代错误消息的数据数组. 当然如果你觉得这里算不上是一个严重的错误, 你可以使用 addWarning() 这个方法.

8. 代码明细:DisallowHashCommentsSniff.php

我们在上面说到的 DisallowHashCommentsSniff 嗅探规则就应该是如下的样子:
<?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 XWSoulStandard_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 假定检测的所有代码都是PHP的.你可以指定一个分词器列表来指定你的嗅探规则支持/允许 PHP, JS 或者 XML 文件 或者这三种语言的任意混合. 你可以通过设置你的嗅探规则成员变量 $supportedTokenizers 来做到这点. 将以下代码添加到你的嗅探规则里意味着, 告诉 PHP_CodeSniffer 它可以对 PHP 和 JS 都进行检测:
<?php
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);

9. 运行检测

phpcs --standard=XWSoulStandard Test.php