认识PHP_CodeSniffer

PHP_CodeSniffer是PEAR中的一个用PHP5写的用来检查嗅探PHP代码是否有违反一组预先设置好的编码标准的一个包,它是确保你的代码简洁一致的必不可少的开发工具,甚至还可以帮助程序员减少一些语义错误。

安装PHP_CodeSniffer

如果你的WEB服务器上已经安装了PHP和PEAR,那么直接运行下列命令就可以安装PHP_CodeSniffer。

$ pear install PHP_CodeSniffer

    安装后,你可以在命令行敲入命令phpcs来调用PHP_CodeSniffer检查代码,默认情况下,PHP_CodeSniffer采用的是PEAR的
编码标准;你现在就可以尝试一下用它来检查你的代码是否符合编码标准,如:

$ phpcs /path/to/code/yourfile.php

如果要检查一个目录,则应使用下列命令:

$ phpcs /path/to/code

PHP_CodeSniffer的使用方法

    使用PHP_CodeSniffer的主程序是phpcs,它的具体使用方法我们可以通过在命令行运行下列命令来获得phpcs的相关参数及
相应的使用信息。

$ phpcs -h

使用PHP_CodeSniffer检查文件或文件目录

使用PHP_CodeSniffer的最简单的方式是直接给phpcs传递一个路径。如果路径是指向一个文件目录,那么PHP_CodeSniffer会检查该文件目录及其也有了目录中的所有文件。如果你不希望检查所有子目录,你可以加上-l(locally)参数,这样它只会检查当前目录中的文件。

$ phpcs /path/to/code/myfile.inc
$ phpcs /path/to/code/my_dir

你也可以同时指定多个文件或目录以供PHP_CodeSniffer检查。

$ phpcs /path/to/code/myfile.inc /path/to/code/my_dir

控制PHP_CodeSniffer输出的检查结果

PHP_CodeSniffer在检查完文件之后会给你一个错误报告,列出所有文件中违反编码标准的错误和警告信息。

如果你不想在输出的检查结果中包含警告信息(WARNING),你可以给phpcs加上-n参数。

默认情况下,检查完文件后PHP_CodeSniffer会输出所有的错误和警告信息的列表,通常是很长的,尤其是在检查比较多的文件的时候,列表很长而且速度很快,你根本看不到什么。这个时候你可能只需要知道每个文件各自都有多少个错误和警告信息,你可以使用 –report=summary 参数。

$ phpcs –report=summary /path/to/code

此外,PHP_CodeSniffer还可以将检查结果以xml,csv和类似于Checkstyle输出结果的格式输出,这样方便你在你自己的脚本中对检查结果进步分析处理或者提供给支持Checkstyle的应用程序处理。

$ phpcs –report=checkstyle /path/to/code
$ phpcs –report=csv /path/to/code
$ phpcs –report=checkstyle /path/to/code

注:在以csv格式输出检查结果时,输出结果的第一行指示了检查结果信息的顺序(即哪一列对就着什么信息)。

另外,PHP_CodeSniffer在处理过程中不输出任何信息直到处理结束输出检查结果。在处理比较多的文件时,有可能需要等待比较长的时间,如果你想知道在此过程中发生了什么事情,你可以在执行PHP_CodeSniffer时带上-v参数,这样PHP_CodeSniffer在处理过程中会输出当前正在处理的文件的名称,所包含的行数和标识符的个数。

$ phpcs /path/to/code/CodeSniffer -v

设置PHP_CodeSniffer的相关配置选项

PHP_CodeSniffere有提供一些配置选项,某些编码标准只有在设置了配置选项后才能使用。要设置一个HP_CodeSniffer配置选项,可以使用:

$ phpcs –config-set <option> <value>

你可以使用下列命令删除任意的配置选项,使它恢复到初始值。

$ phpcs --config-delete <option>

使用下列查看配置选项的当前设置

$ phpcs –config-show

下列是PHP_CodeSniffer的配置选项列表及其设置方法:

default_standard  默认的编码标准
report_format     默认的错误报告格式
show_warnings     是否默认显示错误报告中的警告(WARNING)信息
tab_width         默认的tab宽度相当于几个空格
zend_ca_path      Zend编码标准所使用的配置选项,指向Zend Code Analyzer(Zend编码分析器)所在的路径

默认情况下,如果命令行没有指定编码标准,PHP_CodeSniffere会使用PEAR作为编码标准。你可以通过下列命令来修改默认的编码标准。

$ phpcs –config-set default_standard Zend

默认情况下,即如果命令行没有提供报表输出格式的参数,PHP_CodeSniffere会使用完全错误报表格式输出检查结果。你可以通过下列命令来修改检查结果的默认输出格式。

$ phpcs –config-set report_format summary

默认情况下,PHP_CodeSniffere输出结果会输出错误和警告信息,你可以在执行phpcs带上-n去掉警告信息,也可以通过设定show_warnings来决定默认是要显示还是不显示。

$ phpcs –config-set show_warnings 0

默认情况下,PHP_CodeSniffere不会将检查过的文件中的tab替换成空格。在命令行指定一个tab宽度可以使用PHP_CodeSniffere在检查时替换掉被检查文件中的tab。你可以设定config-set使PHP_CodeSniffere默认替换tab成空格。

$ phpcs –config-set tab_width 4

注:在PHP_CodeSniffere已经被设定成默认替换tab成空格时,在命令行指定tab_width为0会让PHP_CodeSniffere停止替换tab。

Zend编码标准包含一个使用Zend Code Analyzer(Zend编码分析器)的sniff,使用zend_ca_path配置选项告诉Zend Code Analyzer的路径。

$ phpcs –config-set zend_ca_path /path/to/ZendCodeAnalyzer

指定PHP_CodeSniffer所需检查的文件类型(扩展名)

默认情况下,PHP_CodeSniffer会检查.inc和.php文件。也就是说,如果你指定了一个目录让PHP_CodeSniffer检查,那么只有以inc和php为扩展名的文件才会被检查。你可以在命令行通过extensions参数来设置哪一类扩展名的文件需要PHP_CodeSniffer检查。

$ phpcs –extensions=php /path/to/code         只检查该目录中以php为扩展名的文件

$ phpcs –extensions=php,inc,lib /path/to/code 检查该目录中所有以php,inc,lib为扩展名的文件

注:此参数对在命令行直接指定文件(而不是文件目录)的情况不适用,因为这种情况下PHP_CodeSniffer将会忽略掉所有文件的扩展名。

$ phpcs –extensions=inc sgl_06x/www/index.php sgl_06x/www/rpc/server.php 虽然指定了inc,仍然会检查php文件

忽略掉文件的扩展名是PHP_CodeSniffer的一个特性,也是唯一的一种检查没有扩展名文件的方法。如果给phpcs的是一个文件目录作为参数,那么这个目录中的所以没有扩展名的文件将不会被检查,如果要检查,只能一个一个检查。

让PHP_CodeSniffer在检查大量文件时忽略掉其中的某些特殊的文件

在PHP_CodeSniffer对大量文件进行检查时,我们可能不想PHP_CodeSniffer检查其中的某些特殊的文件。你可以使用ignore参数指定文件名的匹配模式,所有文件名与模式匹配的文件都会被忽略掉而不检查。

$ phpcs –ignore=*/tests/*,*/data/* /path/to/code

PHP_CodeSniffer所使用的编码标准

PHP_CodeSniffer中的编码标准是指一系列sniff文件的集合,每个sniff文件对应某个编码标准的特定部分(如代码对齐,或注释)。PHP_CodeSniffer中可以同时安装有多个编码标准,也就是说你可以用PHP_CodeSniffer对多个项目应用各自的编码标准进行检查。

用下列命令可以查看PHP_CodeSniffer中已经安装的编码标准: 

$ phpcs -i

The installed coding standards are PHPCS, Squiz, MySource, PEAR and Zend


PHP_CodeSniffer中的编码标准都在XXX/CodeSniffer/Standards目录下各自有一个以标准名称命名的独立目录,如现在PHP_CodeSniffer安装目录下已经安有PHPCS,Squiz,MySource,PEAR,Zend目录。每个目录下都包含了sniff文件目录,其中包含了用来定义编码标准各个部分的sniff文件,也可以用子目录将sniff文件分类存放。所以可以使用下列命令在PHP_CodeSniffer创建一个名为Seagull的编码标准的目录结构:

$ cd XXX/CodeSniffer/Standards
$ mkdir Seagull
$ mkdir Seagull/Sniffs


除此之外,我们还需要在Seagull目录下创建一个类文件,向PHP_CodeSniffer说明关于这个标准的基本信息并告诉PHP_CodeSniffer这个目录中包含这个标准的sniffs文件。这个类文件必须以此编码标准的名称为前缀,CodingStandard为后缀:

$ touch Seagull/SeagullCodingStandard.php

内容应该如下:

  1. <?php
  2. /**
  3. * Seagull Coding Standard.
  4. *
  5. * PHP version 5
  6. *
  7. * @category  PHP
  8. * @package   PHP_CodeSniffer
  9. * @author    Your Name <you@domain.net>
  10. * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  11. * @version   CVS: $Id: coding-standard-tutorial.xml,v 1.6 2007/10/15 03:28:17 squiz Exp $
  12. * @link      http://pear.php.net/package/PHP_CodeSniffer
  13. */
  14. require_once ‘PHP/CodeSniffer/Standards/CodingStandard.php’;
  15. /**
  16. * Seagull Coding Standard.
  17. *
  18. * @category  PHP
  19. * @package   PHP_CodeSniffer
  20. * @author    Your Name <you@domain.net>
  21. * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  22. * @version   Release: @package_version@
  23. * @link      http://pear.php.net/package/PHP_CodeSniffer
  24. */
  25. class PHP_CodeSniffer_Standards_Seagull_SeagullCodingStandard extends PHP_CodeSniffer_Standards_CodingStandard
  26. {
  27. }//end class
  28. ?>

注:上面的这个代码示例中类的实体是空的。你也可以添加方法,详细的后面会介绍。

这样你就可以往Seagull编码标准的sniff文件目录中添加自己定义的sniff文件了,当然为了查找方便,你可以在Sniff文件目录中创建子目录将不同目的的sniff文件分门别类。

创建sniff文件

每个sniff对应一个PHP文件,文件命名最好是描述该sniff所强调的编码标准格式,并以Sniff.php结尾。比如我们不想让代码中使用#注释,我们可以创建一个名为DisallowHashCommentsSniff.php,并将它归入到Commenting子目录下。


$ cd XXX/CodeSniffer/Standards/Seagull/Sniffs
$ mkdir Commenting
$ touch Commenting/DisallowHashCommentsSniff.php

每个sniff类必须实现PHP_CodeSniffer_Sniff接口,这样在调用时PHP_CodeSniffer才会创建一个sniff实例,PHP_CodeSniffer_Sniff定义两个在sniff文件中的类必须要实现的方法:register和process。

sniff通过调用register告诉PHP_CodeSniffer它要检查编码标准哪些方面(也就是这个sniff要处理或者说会处理哪些类型的token)。这样当PHP_CodeSniffer在检查代码文件时碰到这些token时就会调用process方法来处理,同时付给process两个参数,第一个是PHP_CodeSniffer_File 对象(即当前正在被处理的代码文件),另一个是出现错误的token在token堆栈中的索引。
什么是token堆栈?我的认识是类似于php在解析php代码时会先将php代码分解成一个token数组。在sniff文件中通过调用PHP_CodeSniffer_File对象的getTokens()可以获取一个token数组,数组元素是以每个token在堆栈中的位置为索引。所有的token对应一个数组,包含codetypecontent索引对应的元素。其中code索引对应的元素是这个token类型对应的唯一整数值,type则对应token类型的字符串名称,type还以一个与此字符串同名的全局常量。content索引的值是这个token所对应的代码。
注:有些token可能拥有更多的的元素。可以查看PHP/CodeSniffer/File.php中的类类注释有一个token列表和相应的索引说明。
这样我们就可以写一个自己的sniff文件了,先通过register向PHP_CodeSniffer注册此sniff要检查的token类型,然后在process里按照自已需要的编码标准进行处理,如果有错误发生就可以调用PHP_CodeSniffer_File 对象的addError来指示出错,其实也就是在PHP_CodeSniffer的检查结果中增加一条错误信息。当然如果错误不是太严重,你可以调用addWarning来输出一个警告信息。

下面代码示例了不允许在代码中使用类似PERL的#开头的单选注释:

  1. <?php
  2. /**
  3. * PHP_CodeSniffer tokenises PHP code and detects violations of a
  4. * defined set of coding standards.
  5. *
  6. * PHP version 5
  7. *
  8. * @category  PHP
  9. * @package   PHP_CodeSniffer
  10. * @author    Your Name <you@domain.net>
  11. * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  12. * @version   CVS: $Id: coding-standard-tutorial.xml,v 1.6 2007/10/15 03:28:17 squiz Exp $
  13. * @link      http://pear.php.net/package/PHP_CodeSniffer
  14. */
  15. require_once ‘PHP/CodeSniffer/Sniff.php’;
  16. /**
  17. * This sniff prohibits the use of Perl style hash comments.
  18. *
  19. * An example of a hash comment is:
  20. *
  21. * <code>
  22. *  # This is a hash comment, which is prohibited.
  23. *  $hello = ‘hello’;
  24. * </code>
  25. *
  26. * @category  PHP
  27. * @package   PHP_CodeSniffer
  28. * @author    Your Name <you@domain.net>
  29. * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  30. * @version   Release: @package_version@
  31. * @link      http://pear.php.net/package/PHP_CodeSniffer
  32. */
  33. class Seagull_Sniffs_Commenting_DisallowHashCommentsSniff implements PHP_CodeSniffer_Sniff
  34. {
  35. /**
  36. * Returns the token types that this sniff is interested in.
  37. *
  38. * @return array(int)
  39. */
  40. public function register()
  41. {
  42. return array(T_COMMENT);
  43. }//end register()
  44. /**
  45. * Processes the tokens that this sniff is interested in.
  46. *
  47. * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found.
  48. * @param int                  $stackPtr  The position in the stack where
  49. *                                        the token was found.
  50. *
  51. * @return void
  52. */
  53. public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
  54. {
  55. $tokens = $phpcsFile->getTokens();
  56. if ($tokens[$stackPtr][‘content’]{0} === ‘#’) {
  57. $error = ‘Hash comments are prohibited’;
  58. $phpcsFile->addError($error, $stackPtr);
  59. }
  60. }//end process()
  61. }//end class
  62. ?>

Coding Standard类方法

正如之前所提到的,每个编码标准都有一个Coding Standard类,PHP_CodeSniffer 通过这个类获取关于这个编码标准的信息,同时这个类也向PHP_CodeSniffer标识了它亿在的这个文件目录中包含有sniff文件。我们可以通过重写这个类的两个方法来提供关于这个编码标准的额外信息。

  • getIncludedSniffs()
    这个方法可以告诉PHP_CodeSniffer除了这个编码对应的sniffs文件之外,你还要加载来自其它编码标准的sniff文件。它可以包含单个sniff文件,也可以包含整个目录或整个编码标准对应的sniff文件。
    看如下示例:

    1. <?php
    2. /**
    3. * Return a list of external sniffs to include with this standard.
    4. *
    5. * The MyStandard coding standard uses some generic sniffs, and
    6. * the entire PEAR coding standard.
    7. *
    8. * @return array
    9. */
    10. public function getIncludedSniffs()
    11. {
    12. return array(
    13. ‘PEAR’,
    14. ‘Generic/Sniffs/Formatting/MultipleStatementAlignmentSniff.php’,
    15. ‘Generic/Sniffs/Functions’,
    16. );
    17. }//end getIncludedSniffs()
    18. ?>

    注:在用此方法包含另外一个编码标准的的sniff文件时,如果这个标准本身又有包含其它编码标准的sniff文件时,这些sniff文件也会被包含进来。所以,基于一个编码标准创建一个新的编码标准是非常方便的。

  • getExcludedSniffs()

    这个方法的作用和上面的方法相反。看下面的示例,先是包含了某个整个的编码标准的sniff文件,但是又不需要其中的某个sniff文件。

    1. <?php
    2. /**
    3. * Return a list of external sniffs to include with this standard.
    4. *
    5. * The MyStandard coding standard uses all PEAR sniffs except one.
    6. *
    7. * @return array
    8. */
    9. public function getIncludedSniffs()
    10. {
    11. return array(
    12. 'PEAR',
    13. );
    14. }//end getIncludedSniffs()
    15. /**
    16. * Return a list of external sniffs to exclude from this standard.
    17. *
    18. * The MyStandard coding standard uses all PEAR sniffs except one.
    19. *
    20. * @return array
    21. */
    22. public function getExcludedSniffs()
    23. {
    24. return array(
    25. 'PEAR/Sniffs/ControlStructures/ControlSignatureSniff.php',
    26. );
    27. }//end getExcludedSniffs()
    28. ?>