本連載では第一線の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::ProhibitTwoArgOpenopen $fh, "< $file"と書くより、open $fh, '<', $fileと書くModules::RequireFilenameMatchesPackagepackage Foo::Barというパッケージであれば、Bar.というファイル名である必要があるpm Subroutines::ProhibitExplicitReturnUndef- 失敗時の返却は、
return undefと書くより、returnと書く Subroutines::ProhibitReturnSort- スカラコンテキストで
sortが呼び出されたときの挙動は定まっていないため禁止 Variables::ProhibitConditionalDeclarationsmy $foo = $bar if $baz;という変数宣言を禁止BuiltinFunctions::ProhibitStringyEvaleval "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

