本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは小林謙太さんで、
堅牢な開発とは、
本稿は、
Perlに約束を入れて、異常を予防する
異常が起きてから対処するより、
checkオプションで構文チェックを行う
Perlで書いたプログラムの構文が正しいかを確認するには、perl -c foo.
のように-c
オプションを付けてプログラムを実行します。perldoc perlrun
に書いてあるとおり、BEGIN
、UNITCHECK
、CHECK
ブロックのコードを実行します。たとえば次のコードでperl -c
した場合、BEGIN
ブロックの中が実行されてHELLO
と標準出力して、pprint
の記述が構文エラーであると指摘します。こういったブロックにアプリケーションロジックを記述することは、
BEGIN {
print "HELLO\n" # print HELLO
}
pprint "WORLD\n" # error! NOT print
一方、
プラグマを利用し、より厳しい構文チェックを行う
Perlインタプリタにヒントを与えるためのしくみとして、$^{WARNING_
やヒントハッシュと呼ばれる$^H
、%^H
といった組込みの変数があり、
BEGIN {
$^H{"main/myrand"} = rand;
}
sub hello {
# caller
my $hinthash = (caller(0))[10];
if ($hinthash->{"main/myrand"} < 0.5) {
print "HELLO";
}
}
hello; # HELLOをランダムに出力
このコードでは、BEGIN
ブロックで抽選した値に応じ、
まずは標準プラグマのstrict
プラグマとwarnings
プラグマを紹介します。これらのプラグマは本連載第7回
strictプラグマで、安全でない構文を制限する
strict
プラグマを使わなかった場合、$var
の値を"var"
という文字列だけで参照できます。柔軟ですが、strict
プラグマを利用し、
our $var = "foo";
print ${"var"}; # 文字列 "var" のデリファレンス
# => foo
{
use strict;
print ${"var"}
# => 致命的なエラー:
# Can't use string ("var") as a SCALAR ref while "stri
ct refs" in use
# at src/3_strict_refs_example.pl line 7.
}
strict
プラグマがほかに制限できる構文について、perldoc strict
を参照してください。
warinigsプラグマで、警告ではなく致命的なエラーを選択する
warnings
プラグマも、strict
プラグマと同様に意図せぬ挙動になりがちなPerlの構文を制限します。たとえば、
use warnings;
my $total = "foo" + 123;
print $total;
# =>
# Argument "foo" isn't numeric in addition (+)
# at bar.pl line 2.
123
しかし、warnings
プラグマは、FATAL
オプションにどの警告カテゴリを昇格させるかを指定します。
use warnings FATAL => 'numeric';
my $total = "foo" + 123;
print $total;
# =>
# Argument "foo" isn't numeric in addition (+)
# at bar.pl line 2.
このコードでは、FATAL
オプションに警告カテゴリのnumeric
を指定しています。これにより、FATAL
オプションにall
を指定することで、
表1に、perldoc perldiag
を参照してください。
警告のカテゴリ | 内容 |
---|---|
numeric | 文字列が、 |
experimental | signaturesといったPerlの実験的な機能の使用 |
deprecated | Perlで非推奨となった機能の使用 |
once | 一意な変数名がtypo |
redefine | サブルーチンを再定義したとき |
exec | system() やexec() などで、 |
recursion | 無限再帰の可能性がある |
newline | ファイル名に改行文字が含まれる可能性がある |
portable | 32ビットで表現できる数値を超えている |
stricturesプラグマで、バランスの良い約束を入れる
構文チェックを厳しくするためにwarnings FATAL =>'all'
を行うのも手ですが、recursion
は無限再帰の可能性を示しているだけで、strictures
です。strictures2.
を用いて説明します。
use strictures 2; # バージョン2を指定
上記は、
use strict;
use warnings FATAL => 'all';
use warnings NONFATAL => qw(
exec
recursion
internal
malloc
newline
experimental
deprecated
portable
);
no warnings 'once';
strictures 2
を指定して、PERL_
環境変数も有効にすると、
no indirect 'fatal'; ――(1)
no multidimensional; ――(2)
no bareword::filehandles; ――(3)
new Foo
といった間接オブジェクト記法を禁止し、$hash{1,2}
といったキーに配列を利用した呼び出しを禁止し、<FH>
といったBareword
によるファイルハンドルの扱いを禁止します。
構文チェックを一部外す方法
未然に問題に気付くために構文チェックは厳しいほうがよいと思いますが、
use warnings FATAL => 'numeric';
{
no warnings 'numeric';
my $total = "foo" + 111 # NO error
}
my $total = "foo" + 111 # ERROR!!
Perl::Criticで静的検査を行う
次に、Perl::Critic
1.
なお、
perlcriticを使ってみる
Perl::Critic
に同封されるperlcritic
と呼ばれるコマンドラインツールを使ってみましょう。実際の用途としては、
次のコードは、strict
が有効になっていないため警告が出ています。
% echo "print 'HELLO'" | perlcritic
Code before strictures are enabled at line 1, column 1. S
ee page 429 of PBP. (Severity: 5)
strict
プラグマを有効にして、
% echo "use strict;print 'HELLO'" | perlcritic
source OK
先ほどの警告にあった(Severity: 5)
は静的検査の厳しさを指し、3
と指定した場合の例です。
% echo "use strict;print 'HELLO'" | perlcritic -3
Code not contained in explicit package at line 1, column
1. Violates encapsulation. (Severity: 4)
Module does not end with "1;" at line 1, column 12. Must
end with a recognizable true value. (Severity: 4)
Code before warnings are enabled at line 1, column 12. Se
e page 431 of PBP. (Severity: 4)
この指摘の意味はそれぞれ、1;
で終わっていない、warnings
プラグマを利用していない、
.perlcriticrcで、静的検査のルールを指定する
Perl::Critic
では、~/.perlcriticrc
ファイルに静的検査の設定を記述します。基本的な設定例は次のとおりです。
severity = 3 ――(1)
theme = security || bugs ――(2)
[-Subroutines::ProhibitSubroutinePrototypes] ――(3)
Subroutines::ProhibitSubroutinePrototypes
の先頭に-を添え、
静的検査チェックのルール一覧を確認する
適用可能な静的検査のルール一覧はperlcritic --list
で取得でき、.perlcriticrc
を適用したルール一覧はperlcritic --list-enabled
で取得できます。各ルールの概要をつかむのであれば、Perl::Critic::PolicySummary
を読むことをお勧めします。以下、
InputOutput::ProhibitTwoArgOpen
open $fh, "< $file"
と書くより、open $fh, '<', $file
と書くModules::RequireFilenameMatchesPackage
package Foo::Bar
というパッケージであれば、Bar.
というファイル名である必要があるpm Subroutines::ProhibitExplicitReturnUndef
- 失敗時の返却は、
return undef
と書くより、return
と書く Subroutines::ProhibitReturnSort
- スカラコンテキストで
sort
が呼び出されたときの挙動は定まっていないため禁止 Variables::ProhibitConditionalDeclarations
my $foo = $bar if $baz;
という変数宣言を禁止BuiltinFunctions::ProhibitStringyEval
eval "my $foo; bar($foo);
と書くより、eval { my $foo;bar($foo) }
と書く
ここまでに、Perl::Critic
を用いて未然に異常を防ぐ方法を紹介しました。アプリケーションによらない汎用的な解決手段ですので、
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、 NFT