関数の動的生成と呼び出しは、Perlの最も柔軟なメカニズムの1つで、ラテックスやシェルスクリプトの伝統を受け継いでいます。早いバージョンから、Perlは文字列による関数の呼び出し(シンボリックリファレンス/グロブを介して)、変数や連想配列内にサブプログラムへの参照を保持すること、さらにはAUTOLOAD構文を用いて瞬時に関数を生成することを許可しています。
このアプローチの主な問題は、セキュリティ(置換された文字列による望ましくない関数の呼び出しの可能性)とパフォーマンス(シンボリック名の解決は直接呼び出しより遅い)です。また、関数のスコープ管理や引数の数が正しいことを確認することも重要です。
解決策は、ハッシュディスパッチャー(文字列/キーワードからコードリファレンスへのマッピング)を使用し、ユーザーのコードを実行するためにevalを避け、呼び出しを許可された関数のリストを明確に定義することです。
キーによるディスパッチのコード例:
my %dispatch = ( add => sub { $_[0] + $_[1] }, sub => sub { $_[0] - $_[1] }, mult => sub { $_[0] * $_[1] }, ); my $key = 'add'; if (exists $dispatch{$key}) { print $dispatch{$key}->(2, 3); # 5を出力します } else { die "未知のアクション $key"; }
主な特徴:
文字列を使って関数をその名前で呼び出すことはできますか?
回答: はい、できますが危険です — $fn_name->()の呼び出しや直接のシンボリックリファレンス(&$fn_name();など)は外部(ユーザー)データとともに使用することは推奨されません。これは潜在的な脆弱性を引き起こします。
Perlにおけるコードリファレンスと関数名の違いはありますか?
回答: はい、関数名は常にグローバルですが、関数へのリファレンス(コードリファレンス)はレキシカル、ローカルであり、サブプログラム間で渡したり、匿名関数を保持したりできます。
my $coderef = sub { ... }; my $named = \&fn_name;
ハッシュディスパッチャーを介して存在しない関数を呼び出すとどうなりますか?
回答: キーが存在しない場合、エラーが発生します。したがって、呼び出しの前にexistsのチェックを行い、未認識のコマンドを処理する必要があります。さもなければ、undefを呼び出そうとして致命的なエラーが発生します。
ウェブサイトのコマンドがGETパラメータから関数の名前を受け取り、evalを介して呼び出します — どのユーザーでもsystem、unlinkなどの危険な関数を呼び出すことができます。
利点:
欠点:
関数のホワイトリストを持つハッシュを使用し、すべてのインプットを検証し、evalは使用せず、エラーはキャッチしてログに記録します。
利点:
欠点: