使用PHP_CodeSniffer定制规则

在了解了词法分析的原理后(​​探究PHP_CodeSniffer的代码静态分析原理(一)​​),我们现在尝试使用PHP_CodeSniffer定制一条简单的规则——“禁止使用#号进行单行注释”。 有问题的测试代码:

<?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编写新规则呢?

1. 规则库目录介绍

首先PHP_CodeSniffer的所有规则都存放在​​/src/Standards/​​​目录下,默认该目录下已经有Generic、PEAR、PSR1、PSR2、PSR12、Squiz、Zend等目录,每一个目录其实就是一个规则库。如果想使用其中某一个规则库,例如PEAR规则库,运行时加入参数​​--standard=D:/git/PHP_CodeSniffer/src/Standards/PEAR​​,扫描时就会使用该规则库进行扫描。

2. 创建新规则库目录

我们在​​/src/Standards/​​​目录下新建一个文件夹,命名为​​FireLine​​​,即规则库名为​​FireLine​​​,然后在​​FireLine​​​文件夹中新建​​Sniffs​​文件夹和ruleset.xml文件。其中ruleset.xml的内容如下:

<?xml version="1.0"?>
<ruleset name="FireLine">
<description>360 FireLine rule for test.</description>
</ruleset>

里面定义了规则库的名称和描述。

3. 创建规则实现文件

然后在​​Sniffs​​​文件夹中新建​​Commenting​​​文件夹,代表了一个更细的注解分类,接着这个文件夹里面新建php文件​​DisallowHashCommentsSniff.php​​(每个规则实现文件对应一个Sniff结尾的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 https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
* @link

namespace PHP_CodeSniffer\Standards\FireLine\Sniffs\Commenting;

use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;

class DisallowHashCommentsSniff implements Sniff{
/**
* Returns the token types that this sniff is interested in.
*
* @return
public function register()
{
return array(T_COMMENT);
}//end register()

/**
* 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
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['content']{0} === '#') {
$error = '禁止使用#号进行单行注释;扫描发现 %s';
$data = array(trim($tokens[$stackPtr]['content']));
$phpcsFile->addError($error, $stackPtr, 'Found', $data);
}
}//end process()
}//end class
4. 规则实现详解

首先每个sniff类必须实现Sniff接口,该接口内有两个必须要实现的方法:register()和process()方法。

首先通过调用register()方法告诉PHP_CodeSniffer我们要检查编码标准哪些方面(也就是我们要查找哪些类型的TOKEN)。然后当词法解析引擎碰到这些TOKEN时就会调用process()方法来做进一步处理。

在该文件中,我们可以看到register()方法中是想查找T_COMMENT类型的TOKEN,通过PHP官网提供的TOKEN列表中得到T_COMMENT对应的PHP语法为​​// 或 #,以及 PHP 5 下的 /* */​​,即PHP语法中的单行注释。所以说,当词法解析引擎遇到单行注释类别的TOKEN时,就会自动继续调用process()方法。

我们接着来看process()方法,该方法有两个参数,第一个是​​$phpcsFile​​​对象,即当前正在被处理的代码文件对象;第二个是​​$stackPtr​​​参数,这个参数的意思是我们当前关注的TOKEN-即代表着单行注释的TOKEN(T_COMMENT)在TOKEN序列中的索引。这里正好回应了上篇文章提到的PHP词法分析原理,将PHP源文件解析成一个TOKEN序列,而​​$stackPtr​​参数表示当前TOKEN在这个TOKEN序列的索引位置。

接下来是process()方法内的实现,首先通过PHP_CodeSniffer封装的​​getTokens()​​​方法来获得当前文件的TOKEN序列。在通过索引获取到我关注的T_COMMENT对应的TOKEN后,进一步获取TOKEN数组里面的​​content​​索引对应的内容。

TOKEN数组里面包含了​​code、type、content​​​这三种索引,分别对应的内容是​​TOKEN代号唯一值、TOKEN代号即T_COMMENT、TOKEN所对应的代码​​​。所以判断条件里面的​​$tokens[$stackPtr]['content']{0}​​​的意思是取TOKEN序列中我们所关注的T_COMMENT对应的TOKEN,然后取这个TOKEN中对应的代码中的第一个字符。如果这个字符是​​#​​​,说明触发了单行注释禁止使用​​#​​号的规则。我们最后通过addError()方法来记录触发规则的TOKEN和对应的代码,以及我们的规则解释。

5. 规则运行

规则实现完成后,我们运行一下看一下效果:

php D:/git/PHP_CodeSniffer/bin/phpcs
--standard=D:/git/PHP_CodeSniffer/src/Standards/FireLine
D:/git/PHP_CodeSniffer/src/Standards/FireLine/Tests
--report=xml --report-file=E:/RedlineReport/php_report01.xml

其中​​--standard​​​参数就是指定运行我们自定义的FireLine规则库。 ​​​D:/git/PHP_CodeSniffer/src/Standards/FireLine/Tests​​目录中存放了有问题的测试代码文件。 最后生成的XML报告文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<phpcs version="3.3.1">
xml version="1.0" encoding="UTF-8"?>
<file name="D:\git\PHP_CodeSniffer\src\Standards\FireLine\Tests\Commenting\test01.php" errors="3" warnings="0" fixable="0">
<error line="3" column="1" source="FireLine.Commenting.DisallowHashComments.Found" severity="5" fixable="0">禁止使用#号进行注释;扫描发现 # Check for valid contents.</error>
<error line="7" column="5" source="FireLine.Commenting.DisallowHashComments.Found" severity="5" fixable="0">禁止使用#号进行注释;扫描发现 # Value needs to be an array.</error>
<error line="9" column="9" source="FireLine.Commenting.DisallowHashComments.Found" severity="5" fixable="0">禁止使用#号进行注释;扫描发现 # Error.</error>
</file>
</phpcs>

从报告中可以看到,之前准备测试代码文件中的三处错误,都能成功检查出来。

参考文章: Coding Standard Tutorial

Qtest是360旗下的专业测试团队!

是WEB平台部测试技术平台化、效率化的先锋力量!