使用 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 可以成為一種寶貴的工具,無論你採用何種最佳實踐策略,都可以幫助團隊保持編碼的一致性和易維護性。