使用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 http://pear.php.net/package/PHP_CodeSniffer
*/
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 array(int)
*/
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 void
*/
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平台部测试技术平台化、效率化的先锋力量!
陪伴是最长情的告白
每日为你推送最in的测试技术
识别二维码
关注我们