使用 PerlCritic

如果你想为自己或你的团队开始实施最佳实践,那么 Perl::Critic 是最好的起点。该模块基于 Damien Conway 的 Perl Best Practices 一书,并在实施其中提出的建议方面做得相当不错。

注意: 我应该提及(并且 Conway 自己在书中说)这些是建议。我发现这本书在大多数情况下提供了坚实的推理,但我当然不同意所有这些。需要记住的重要一点是,无论你决定采用何种做法,都要保持一致。代码越可预测,维护起来就越容易。

你还可以通过 perlcritic.com 上的浏览器试用 Perl::Critic。

安装

cpan Perl::Critic

这将安装基本规则集和可从命令行调用的 perlcritic 脚本。

基本用法

perlcriticCPAN 文档包含完整的文档,因此我将仅讨论最常见的用例以帮助你入门。基本用法是简单地在文件上调用 perlcritic:

 perlcritic -1 /path/to/script.pl

perlcritic 既适用于脚本,也适用于模块。在 -1 指的是你要对脚本运行的规则的严重性级别。有五个级别对应于 Perl::Critic 将挑选代码的程度。

-5 是最温和的,只会警告可能导致意外结果的潜在危险问题。 -1 是最残酷的,会抱怨你的代码整洁或不整齐的事情。根据我的经验,保持符合 3 级的代码足以避免危险而不会过于苛刻。

默认情况下,任何失败都会列出规则触发的原因和严重性:

perlcritic -3 --verbose 8 /path/to/script.pl

Debugging module loaded at line 16, column 1.  You've loaded Data::Dumper, which probably shouln't be loaded in production.  (Severity: 4)
Private subroutine/method '_sub_name' declared but not used at line 58, column 1.  Eliminate dead code.  (Severity: 3)
Backtick operator used at line 230, column 37.  Use IPC::Open3 instead.  (Severity: 3)
Backtick operator used at line 327, column 22.  Use IPC::Open3 instead.  (Severity: 3)

查看政策

你可以通过使用 perlcritic 的 –verbose 选项快速查看触发的规则以及原因 :

将级别设置为 8 将显示触发警告的规则:

perlcritic -3 --verbose 8 /path/to/script.pl

[Bangs::ProhibitDebuggingModules] Debugging module loaded at line 16, column 1.  (Severity: 4)
[Subroutines::ProhibitUnusedPrivateSubroutines] Private subroutine/method '_sub_name' declared but not used at line 58, column 1.  (Severity: 3)
[InputOutput::ProhibitBacktickOperators] Backtick operator used at line 230, column 37.  (Severity: 3)
[InputOutput::ProhibitBacktickOperators] Backtick operator used at line 327, column 22.  (Severity: 3)

虽然等级 11 将显示规则存在的具体原因:

perlcritic -3 --verbose 11 /path/to/script.pl

Debugging module loaded at line 16, near 'use Data::Dumper;'.
  Bangs::ProhibitDebuggingModules (Severity: 4)
    This policy prohibits loading common debugging modules like the
    Data::Dumper manpage.

    While such modules are incredibly useful during development and
    debugging, they should probably not be loaded in production use. If this
    policy is violated, it probably means you forgot to remove a `use
    Data::Dumper;' line that you had added when you were debugging.
Private subroutine/method '_svn_revisions_differ' declared but not used at line 58, near 'sub _sub_name {'.
  Subroutines::ProhibitUnusedPrivateSubroutines (Severity: 3)
    By convention Perl authors (like authors in many other languages)
    indicate private methods and variables by inserting a leading underscore
    before the identifier. This policy catches such subroutines which are
    not used in the file which declares them.

    This module defines a 'use' of a subroutine as a subroutine or method
    call to it (other than from inside the subroutine itself), a reference
    to it (i.e. `my $foo = \&_foo'), a `goto' to it outside the subroutine
    itself (i.e. `goto &_foo'), or the use of the subroutine's name as an
    even-numbered argument to `use overload'.
Backtick operator used at line 230, near 'my $filesystem_diff = join q{}, `diff $trunk_checkout $staging_checkout`;'.
  InputOutput::ProhibitBacktickOperators (Severity: 3)
    Backticks are super-convenient, especially for CGI programs, but I find
    that they make a lot of noise by filling up STDERR with messages when
    they fail. I think its better to use IPC::Open3 to trap all the output
    and let the application decide what to do with it.

        use IPC::Open3 'open3';
        $SIG{CHLD} = 'IGNORE';

        @output = `some_command`;                      #not ok

        my ($writer, $reader, $err);
        open3($writer, $reader, $err, 'some_command'); #ok;
        @output = <$reader>;  #Output here
        @errors = <$err>;     #Errors here, instead of the console
Backtick operator used at line 327, near 'my $output = `$cmd`;'.
  InputOutput::ProhibitBacktickOperators (Severity: 3)
    Backticks are super-convenient, especially for CGI programs, but I find
    that they make a lot of noise by filling up STDERR with messages when
    they fail. I think its better to use IPC::Open3 to trap all the output
    and let the application decide what to do with it.

        use IPC::Open3 'open3';
        $SIG{CHLD} = 'IGNORE';

        @output = `some_command`;                      #not ok

        my ($writer, $reader, $err);
        open3($writer, $reader, $err, 'some_command'); #ok;
        @output = <$reader>;  #Output here
        @errors = <$err>;     #Errors here, instead of the console

忽略代码

有时你不能遵守 Perl::Critic 政策。在这些情况下,你可以在代码周围包含特殊注释**## use critic()## no critic**,以使 Perl::Critic 忽略它们。只需在括号中添加要忽略的规则(倍数可以用逗号分隔)。

##no critic qw(InputOutput::ProhibitBacktickOperator)
my $filesystem_diff = join q{}, `diff $trunk_checkout $staging_checkout`;
## use critic

确保包装整个代码块,否则 Critic 可能无法识别 ignore 语句。

## no critic (Subroutines::ProhibitExcessComplexity)
sub no_time_to_refactor_this {
    ...
}
## use critic

请注意,某些策略在文档级别上运行,不能以这种方式免除。但是,它们可以关闭……

创建永久例外

使用## no critic() 很不错,但是当你开始采用编码标准时,你可能会想要对某些规则做出永久性例外。你可以通过创建 .perlcriticrc 配置文件来完成此操作。

此文件不仅可以自定义运行哪些策略,还可以自定义它们的运行方式。使用它就像将文件放在主目录中一样简单(在 Linux 中,不确定它是否在 Windows 上是相同的位置)。或者,你可以使用 –profile 选项在运行命令时指定配置文件 :

perlcritic -1 --profile=/path/to/.perlcriticrc /path/to/script.pl

同样, perlcritic CPAN 页面包含这些选项的完整列表。我将列出我自己的配置文件中的一些示例:

应用基本设置:

#very very harsh
severity = 1
color-severity-medium = bold yellow
color-severity-low = yellow
color-severity-lowest = bold blue

禁用规则(请注意策略名称前面的短划线):

# do not require version control numbers
[-Miscellanea::RequireRcsKeywords]

# pod spelling is too over-zealous, disabling
[-Documentation::PodSpelling]

修改规则:

# do not require checking for print failure ( false positives for printing to stdout, not filehandle )
[InputOutput::RequireCheckedSyscalls]
    functions = open close

# Allow specific unused subroutines for moose builders
[Subroutines::ProhibitUnusedPrivateSubroutines]
private_name_regex = _(?!build_)\w+

结论

正确使用 Perl::Critic 可以成为一种宝贵的工具,无论你采用何种最佳实践策略,都可以帮助团队保持编码的一致性和易维护性。