パール:CGI および DBI モジュールの変数スコープの問題
質問
これまでに遭遇したことのない変数スコープの問題と思われる問題に遭遇しました。Perl の CGI モジュールと DBI の do() メソッドの呼び出しを使用しています。少し簡略化したコード構造は次のとおりです。
use DBI;
use CGI qw(:cgi-lib);
&ReadParse;
my $dbh = DBI->connect(...............);
my $test = $in{test};
$dbh->do(qq{INSERT INTO events VALUES (?,?,?)},undef,$in{test},"$in{test}",$test);
#1 のプレースホルダー変数は、初期化されていないものとして評価されます。他の 2 つのプレースホルダー変数は機能します。
質問:%in ハッシュを二重引用符で囲む (#2 プレースホルダー) か、値を新しい変数 (#3 プレースホルダー) に再割り当てしない限り、 do() のコンテキスト内で %in ハッシュを使用できないのはなぜですか?
これは、CGI モジュールの ReadParse() 関数が %in ハッシュにスコープを割り当てる方法と関係があると思いますが、%in がトップレベルでは利用可能であるのに私の環境内では利用できない理由を理解できるほど Perl のスコープをよく知りません。 () 声明。
誰かがスコープの問題を理解している場合、それを処理するより良い方法はありますか?すべての %in 参照を二重引用符で囲むのは少し面倒に思えます。クエリ パラメーターごとに新しい変数を作成するのは現実的ではありません。
明確にしておきたいのですが、私の質問は変数のスコープの問題に関するものです。CGI でクエリ パラメータを取得するには ReadParse() が推奨される方法ではないことを認識しています。
Perl 5.8.8、CGI 3.20、および DBI 1.52 を使用しています。これを読んでいる人にはよろしくお願いします。
@Pi さんと @Bob さん、ご提案ありがとうございます。%in のスコープを事前に宣言しても効果はありません (私は常に strict を使用します)。結果は前と同じです。データベースでは、col1 は null ですが、cols 2 と 3 は期待値に設定されています。
参考までに、ReadParse 関数を示します (下記を参照)。これは CGI.pm の一部である標準関数です。私の理解では、スコープを設定する目的で %in ハッシュを (厳密を満たす以外に) 初期化するつもりはありません。関数がそれを処理しているように私には見えるからです。
sub ReadParse {
local(*in);
if (@_) {
*in = $_[0];
} else {
my $pkg = caller();
*in=*{"${pkg}::in"};
}
tie(%in,CGI);
return scalar(keys %in);
}
私の質問は、 do() のコンテキスト内で %in ハッシュを取得する最良の方法は何でしょうか?再度、感謝します!これが私の元の質問に追加情報を提供する正しい方法であることを願っています。
@ダン:&ReadParse 構文についての意見を聞きました。通常は CGI::ReadParse() を使用しますが、この場合は次の方法に固執するのが最善であると考えました。 CGI.pm ドキュメントにはそれがあります その通り。
解決
DBI ドキュメントによると:現在、関連付けられた変数のバインドは機能しません。
DBI は内部的にかなり複雑で、残念ながら効率を高めるためにいくつかの調整が必要であり、それが問題の原因となっています。私は、醜い古い cgi-lib スタイルのコードを削除するべきだという他の皆さんの意見に同意します。優れたフレームワーク (Catalyst を使用) なしで CGI を実行するのは十分に不快です。
他のヒント
実際には、ドキュメントで説明されているように使用しているようには見えません。https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL
どうしても使用しなければならない場合は、CGI::ReadParse(); を使用してください。より賢明で、あまり悪気のない構文のように思えます。この状況では大きな違いがあるとは思えませんが、これは結合変数なので、それが何をしているのかは誰にもわかりません ;)
より一般的な $cgi->param('foo') 構文を使用できない特別な理由はありますか?これはもう少しクリーンで、かなり予測可能な方法で名前空間を汚します。
use strict;
. 。いつも。
宣言してみてください
our %in;
それが役立つかどうかを確認してください。それに失敗すると、 strict
より有用なエラーが生成される可能性があります。
何が問題なのかはわかりませんが、そうでないことをいくつか言えます。
- それはスコープの問題ではありません。そうでない場合は、次のような例はありません
$in{test}
うまくいくだろう。 - それは古風なものではない
&
呼び出し構文。(これは「正しく」ありませんが、この場合は無害です。)
ReadParse
厄介なコードです。シンボル テーブルを変更して、呼び出し側パッケージ内にグローバル変数 %in を作成します。さらに悪いことに、これは関連付けられた変数であるため、それにアクセスすると (理論的には) 何でもできる可能性があります。CGI.pm のソース コードを見ると、 FETCH
メソッドは単に呼び出すだけです params()
データを取得するメソッド。なぜフェッチされるのか分かりません $dbh->do()
機能していません。
まず、それは do のコンテキスト/範囲内ではありません。それはまだメインまたはグローバルのコンテキスト内にあります。Perl のサブルーチンまたはさまざまな「クラス」に関連する何らかの方法で {} を入力するまで、コンテキストを離れることはできません。() 括弧内ではスコープを離れることはありません。
あなたが私たちに与えたサンプルは初期化されていないハッシュのものであり、Piが示唆したように、strictを使用すると確実にそれらの発生を防ぐことができます。
コードのより代表的な例を教えていただけますか?%IN はどこでどのように設定していますか?
そこは何かがとても壊れています。Perl のスコープ設定は比較的単純なので、何かおかしなことをしない限り、そのような奇妙なものに遭遇する可能性はほとんどありません。提案されているように、strict プラグマ (および警告もオン) に切り替えます。実際には、とにかく両方を使用する必要があります)。
%in がどのように定義されているかを見ることができなければ、何が起こっているのかを知るのはかなり困難です (それは、あの見苦しい ReadParse 呼び出しと関係があるのでしょうか?ところで、なぜ先頭に & を付けて呼び出しているのですか?この構文は長い間廃止され、存在しなくなったと考えられてきました)。何が起こっているかを確認できるように、もう少しコードを投稿することをお勧めします。
使用している DBI のバージョンは何ですか?見てみると、 DBI 変更ログ 1.00 より前のバージョンでは属性引数がサポートされていなかったようです。「初期化されていない」のではないかと思います $in{test}
実際には undef
あなたが渡しているのは $dbh->do()
.
あなたが挙げた例からすると、これは ない スコープの問題、またはどのパラメータも機能しません。
DBI (または DBD、バインド パラメーターがどこで使用されているかは不明) がタイ マジックを尊重していないようです。回避策は、2 番目と 3 番目のパラメータのように、渡す内容を文字列化するかコピーすることです。
SQLite と DBI 1.53 を使用した簡単なテストでは、正常に動作していることがわかります。
$ perl -MDBI -we'sub TIEHASH { bless {} } sub FETCH { "42" } tie %x, "main" or die; my $dbh = DBI->connect("dbi:SQLite:dbname=dbfile","",""); $dbh->do("create table foo (bar char(80))"); $dbh->do("insert into foo values (?)", undef, $x{foo}); print "got: " . $dbh->selectrow_array("select bar from foo") . "\n"; $dbh->do("drop table foo")'
got: 42
使用しているデータベースを共有したいですか?
さて、これを試してみてください:
use CGI; my %in; CGI::ReadParse(\%in);
実際に宣言した変数を使用しているため、スコープを制御できるため、これは役立つかもしれません(さらに、 use strict
水を濁らせる可能性のある他の厄介なものがなければ)
これは次のようになり始めているので、 tie()
問題がある場合は、次の実験を試してください。これを foo.pl として保存し、次のように実行します。 perl foo.pl "x=1"
use CGI;
CGI::ReadParse();
p($in{x}, "$in{x}");
sub p { my @a = @_; print "@a\n" }
印刷されるはずです 1 1
. 。そうでない場合は、犯人が見つかりました。
あなたのテストコードを試してみました http://www.carcomplaints.com/test/test.pl.txt, 、私のコンピュータではすぐに動作し、問題ありません。期待どおり 3 つの値が得られます。CGI としては実行しませんでしたが、次のものを使用しました。
...
use CGI qw/-debug/;
...
コンソールに変数を書きます(test=test
)、スクリプトは問題なく挿入されます。
ただし、これを省略すると、tt は空の文字列と 2 つの NULL を挿入します。これは、文字列に値を補間するためです。これにより、値が次の文字列になります。 $in{test}
それは undef
現時点で。 undef
空の文字列に文字列化され、それがデータベースに挿入されます。
これを試して
%in = ReadParse();
しかし私はそれを疑います。クエリパラメータか何かを取得しようとしていますか?